AVR. Учебный курс. Использование ШИМ

Вот уже несколько раз я ругался странным словом ШИМ. Пора бы внести ясность и разьяснить что же это такое. Вообще, я уже расписывал этот режим работы, но все же повторюсь в рамках своего курса.
 

Вкратце, Широтно Импульсная Модуляция (в буржуйской нотации этот режим зовется PWMPulse Width Modulation) это способ задания аналогового сигнала цифровым методом, то есть из цифрового выхода, дающего только нули и единицы получить какие то плавно меняющиеся величины. Звучит как бред, но тем не менее работает. А суть в чем:
 

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

А вот если двигатель включать на десять секунд каждую минуту, то маховик раскрутится, но далеко не на полную скорость — большая инерция сгладит рывки от включающегося двигателя, а сопротивление от трения не даст ему крутится бесконечно долго.
 

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

Меня скважность (отношение длительности периода к длительности импульса) можно плавно менять эту площадь, а значит и напряжение на выходе. Таким образом если на выходе сплошные 1, то на выходе будет напряжение высокого уровня, в случае моего робота, на выходе из моста L293 это 12 вольт, если нули, то ноль. А если 50% времени будет высокий уровень, а 50% низкий то 6 вольт. Интегрирующей цепочкой тут будет служить масса якоря двигателя, обладающего довольно большой инерцией.
 

 

А что будет если взять и гнать ШИМ сигнал не от нуля до максимума, а от минуса до плюса. Скажем от +12 до -12. А можно задавать переменный сигнал! Когда на входе ноль, то на выходе -12В, когда один, то +12В. Если скважность 50% то на выходе 0В. Если скважность менять по синусоидальному закону от максимума к минимуму, то получим… правильно! Переменное напряжение. А если взять три таких ШИМ генератора и гнать через них синусоиды сдвинутые на 120 градусов между собой, то получим самое обычное трехфазное напряжение, а значит привет бесколлекторные асинхронные и синхронные двигатели — фетиш всех авиамоделистов. На этом принципе построены все современные промышленные привода переменного тока. Всякие Unidrive и Omron Jxx

 
В качестве сглаживающей интегрирующей цепи в ШИМ может быть применена обычная RC цепочка:

 

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

 
Аппаратный ШИМ
В случае ATMega16 проще всего сделать на его ШИМ генераторе, который встроен в таймеры. Причем в первом таймере у нас целых два канала. Так что без особого напряга ATmega16 может реализовать одновременно четыре канала ШИМ.

 
Как это реализовано
У таймера есть особый регистр сравнения OCR**. Когда значение в счётном регистре таймера достигнает значения находящегося в регистре сравнения, то могут возникнуть следующие аппаратные события:

  • Прерывание по совпадению
  • Изменение состояния внешнего выхода сравнения OC**.

 
Выходы сравнения выведены наружу, на выводы микроконтроллера

 

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

 


 

Для Pinboard II разница в подключении невелика. Джамперы тут сгруппированы в один блок. А светодиоды и RC цепочки сгруппированы в левом верхнем углу платы.

Предположим, что мы настроили наш ШИМ генератор так, чтобы когда значение в счетном регистре больше чем в регистре сравнения, то на выходе у нас 1, а когда меньше, то 0.

 
Что при этом произойдет? Таймер будет считать как ему и положено, от нуля до 256, с частотой которую мы настроим битами предделителя таймера. После переполнения сбрасывается в 0 и продолжает заново.

 

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

 

Так что меняя значение в регистре сравнения можно менять скважность ШИМ сигнала. А если пропустить этот ШИМ сигнал через сглаживающую RC цепочку (интегратор) то получим аналоговый сигнал.

 
У таймера может быть сколько угодно регистров сравнения. Зависит от модели МК и типа таймера. Например, у Атмега16

  • Timer0 — один регистр сравнения
  • Timer1 — два регистра сравнения (16ти разрядных!)
  • Timer2 — один регистр сравнения

 
Итого — четыре канала. В новых AVR бывает и по три регистра сравнения на таймер, что позволяет одним МК организовать просто прорву независимых ШИМ каналов.

 
Самих режимов ШИМ существует несколько:

 
Fast PWM
В этом режиме счетчик считает от нуля до 255, после достижения переполнения сбрасывается в нуль и счет начинается снова. Когда значение в счетчике достигает значения регистра сравнения, то соответствующий ему вывод ОСхх сбрасыватся в ноль. При обнулении счетчика этот вывод устанавливается в 1. И все!

 
Частота получившегося ШИМ сигнала определяется просто: Частота процесора 8Мгц, таймер тикает до 256 с тактовой частотой. Значит один период ШИМ будет равен 8000 000/256 = 31250Гц. Вполне недурно. Быстрей не получится — это максимальная скорость на внутреннем 8Мгц тактовом генераторе. Но если переключить FUSE биты на внешний кварц то можно раскачать МК на 16Мгц.

 
Еще есть возможность повысить разрешение, сделав счет 8, 9, 10 разрядным (если разрядность таймера позволяет), но надо учитывать, что повышение разрядности, вместе с повышением дискретности выходного аналогового сигнала, резко снижает частоту ШИМ.

 

Phase Correct PWM
ШИМ с точной фазой. Работает похоже, но тут счетчик считает несколько по другому. Сначала от 0 до 255, потом от 255 до 0. Вывод OCxx при первом совпадении сбрасывается, при втором устанавливается.
Но частота ШИМ при этом падает вдвое, изза большего периода. Основное его предназначение, делать многофазные ШИМ сигналы, например, трехфазную синусоиду. Чтобы при изменении скважности не сбивался угол фазового сдвига между двумя ШИМ сигналами. Т.е. центры импульсов в разных каналах и на разной скважности будут совпадать.

 

 
Еще одна тонкость:
Чтобы не было кривых импульсов, то в регистр сравнения любое значение попадает через буфферный регистр и заносится только тогда, когда значение в счетчике достигнет максимума. Т.е. к началу нового периода ШИМ импульса.

 
Clear Timer On Compare
Сброс при сравнении. Это уже скорей ЧИМ — частотно-импульсно моделированный сигнал. Тут работает несколько иначе, чем при других режимах. Тут счетный таймер тикает не от 0 до предела, а от 0 до регистра сравнения! А после чего сбрасывается.

 

 
В результате, на выходе получаются импульсы всегда одинаковой скважности, но разной частоты. А чаще всего этот режим применяется когда надо таймером отсчитывать периоды (и генерить прерывание) с заданной точностью.

 
Например, надо нам прерывание каждую миллисекунду. И чтобы вот точно. Как это реализовать проще? Через Режим СТС! Пусть у нас частота 8Мгц.

 
Прескалер будет равен 64, таким образом, частота тиков таймера составит 125000 Гц. А нам надо прерывание с частотой 1000Гц. Поэтому настраиваем прерывание по совпадению с числом 125.

 
Дотикал до 125 — дал прерывание, обнулился. Дотикал до 125 — дал прерывание, обнулился. И так бесконечно, пока не выключим.

 
Вот вам и точная тикалка.

 
Нет, конечно, можно и вручную. Через переполнение, т.е. дотикал до переполнения, загрузил в обработчике прерывания заново нужные значение TCNTх=255-125, сделал нужные полезные дела и снова тикать до переполнения. Но ведь через СТС красивей! :)

 
Аппаратура
А теперь контрольные регистры, которыми все это безобразие задается и программируется. Опишу на примере Двухканального FastPWM на таймере 1. В других все похоже. Даташит в зубы и вперед.

 
Итак, тут правят бал регистры TCCR1A и TCCR1B. Гы, кто бы сомневался %)

 
Распишу их по битам.
Регистр TCCR1A, биты COM1A1:COM1A0 и COM1B1:COM1B0. Эта братия определяет поведение вывода сравнения OC1A и OC1B соответственно.

 

COMxx1COMxx0Режим работы выхода
00вывод отцеплен от регистра сравнения и не меняется никак.
01Поведение вывода зависит от режима заданного в WGM, различается для разных режимов (FastPWM, FC PWM, Compar out) и разных МК, надо сверяться с даташитом.
10прямой ШИМ (сброс при совпадении и установка при обнулении счета)
11обратный ШИМ (сброс при обнулении и установка при совпадении)


 
Регистр TCCR1A, биты WGM11 и WGM10 вместе с битами WGM12 и WGM13, находящимися в регистре TCCR1B задают режим работы генератора.
WGM13WGM12WGM11WGM10Режим работы
0101Fast PWM 8 бит
0110Fast PWM 9 бит
0111Fast PWM 10 бит


 
Другие комбинации битов WGM задают режимы Phase Correct PWM и CTC (сброс OCxx при совпадении). Если интересно, то читай даташит, я для себя много интересного там не нашел, кроме Phase Correct PWM. И то мне сейчас важней скорость, а не точность фазы :)

 
После остается только запустить таймер, установив бит CS10 (подсчет тактовых импульсов с делителем 1:1)

 
Пример кода:

 
Попробуем поиграться яркостью светодиодов с помощью ШИМ сигналов. Подключи джамперы, чтобы запитать светодиоды LED1 и LED2

 


 
Для версии Pinboard II все аналогично, с поправкой на другое расположение джамперов:

 

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

 

1
2
3
4
5
6
7
8
9
10
11
12
;FastPWM Init
	SETB	DDRD,4,R16	; DDRD.4 = 1 Порты на выход
	SETB	DDRD,5,R16	; DDRD.5 = 1
 
; Выставляем для обоих каналов ШИМ режим вывода ОС** сброс при совпадении. 
; COM1A = 10 и COM1B = 10
; Также ставим режим FAST PWM 8bit (таймер 16ти разрядный и допускает
; большую разрядность ШИМ сигнала. Вплоть до 10 бит.  WGM = 0101
; Осталось только запустить таймер на частоте МК CS = 001
 
	OUTI 	TCCR1A,2<<COM1A0|2<<COM1B0|0<<WGM11|1<<WGM10	 
	OUTI	TCCR1B,0<<WGM13|1<<WGM12|1<<CS10

 
Готово! Теперь ШИМ таймера1 генерит сигнал на выходаx OC1А и OC1B

 
Закинем в регистры сравнения первого и второго канала число 255/3=85 и 255/2 = 128
Так как ШИМ у нас 8ми разрядный, то заброс идет только в младший разряд. Старший же остается нулем. Но регистры сравнения тут у нас 16ти разрядные поэтому грузить надо оба байта сразу. Не забыв запретить прерывания (это важно!!! ибо атомарный доступ)

 

1
2
3
4
5
6
7
	CLI
	OUTI	OCR1AH,0
	OUTI	OCR1AL,85
 
	OUTI	OCR1BH,0
	OUTI	OCR1BL,128
	SEI

 
Поехали! :)

 
Прошиваем, тыкаемся в ноги микроконтроллера осциллографом — видим следующую картину по каналам:

 

 
Как мы и запланировали. С первого канала длительность импульса в 1/3 периода, а со второго в 1/2
Ну и светодиоды горят с разной яркостью. Один ярче, другой тусклей. Меняя значение в регистрах OCR*** мы можем менять скважность.

 
Давай сделаем так, чтобы светодиод плавно менял свою яркость от нуля до максимума. Как помнишь, у нас там была программа, с мигающем по таймеру0 светодиодом. Немного ее подправим, сделаем так, чтобы по таймеру не светодиод мигал, а менялось значение в регистрах сравнения OCR1A и OCR1B. Причем меняться оно будет в разные стороны :)

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
; Main =========================================================
Main:	LDS	R16,TCNT	; Грузим числа в регистры
	LDS	R17,TCNT+1
 
	CPI	R16,0x10	; Сравниванем побайтно выдержку
	BRCS	NoMatch
	CPI	R17,0x01	; Выдержку сделали поменьше = 0x0110
	BRCS	NoMatch
 
; Если совпало то делаем экшн
Match:	CLI			; Запрет прерываний, т.к. атомарный доступ
 
; Меняем первый канал
; Особенность 16ти разрядных регистров в том, что их надо правильно читать и записывать.
; Читают вначале младший, потом старший байты. Так надо, чтобы младший не успел измениться
; (он ведь может тикать по таймеру) пока читают первым старший.  Укладывают их в обратном
; порядке. Сначала старший, потом младший. Правда для регистров OCR это не имеет большой 
; разницы -- они статичные, а вот для TCNT очень даже!
 
	IN	R16,OCR1AL	; Достали первый байт сравнения
	IN	R17,OCR1AH	; он 16ти разрядный, но старший байт будет 0
 
	INC	R16		; Увеличили
 
	OUT	OCR1AH,R17	; И сунули их обратно
	OUT	OCR1AL,R16
 
; Меняем второй канал
	IN	R16,OCR1BL	; Достали второй байт сравнения
	IN	R17,OCR1BH	; он 16ти разрядный, но старший байт будет 0
 
	DEC	R16		; Уменьшили
 
	OUT	OCR1BH,R17	; И сунули их обратно
	OUT	OCR1BL,R16	
	SEI			; Конец атомарного доступа
 
; Теперь надо обнулить счетчик, иначе за эту же итерацию главного цикла
; Мы сюда попадем еще не один раз -- таймер то не успеет натикать 255 значений
; чтобы число в первых двух байтах счетчика изменилось. 
 
	CLR	R16		; Нам нужен ноль
	CLI			; Таймер меняется и в прерывании. Нужен
				; атомарный доступ. Запрещаем прерывания
	OUT	TCNT0,R16	; Ноль в счетный регистр таймера
	STS	TCNT,R16	; Ноль в первый байт счетчика в RAM
	STS	TCNT+1,R16	; Ноль в второй байт счетчика в RAM
	STS	TCNT+2,R16	; Ноль в третий байт счетчика в RAM
	STS	TCNT+3,R16	; Ноль в первый байт счетчика в RAM
	SEI			; Разрешаем прерывания. 
; Не совпало - не делаем :) 
NoMatch:	NOP
 
	INCM	CCNT		; Шарманка вращается дальше, вхолостую
	JMP	Main



 
А теперь давайте включим режим с точной фазой (WGM = 0001) и посмотрим на то как будет меняться скважность.

 

1
2
	OUTI 	TCCR1A,2<<COM1A0|2<<COM1B0|0<<WGM11|1<<WGM10	 
	OUTI	TCCR1B,0<<WGM13|0<<WGM12|1<<CS10

 

 
ШИМ на прерываниях.
Но вот засада — плата уже разведена, захотелось ШИМ, а выводы OCxx уже задействованы под другие цели.

 
Ничего страшного, малой кровью можно это исправить. Также запускаем ШИМ, только:

  • Отключаем выводы OCxx от регистра сравнения.
  • Добавляем два обработчика прерывания на сравнение и на переполнение. В прерывании по сравнению сбрасываем нужный бит, в прерывании по переполнению счетчика устанавливаем.

Все просто :)

 
Пример:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
;FastPWM Init на прерываниях
 
; ШИМ будет на выводах 3 и 6 порта D
	SETB	DDRD,3,R16	; DDRD.3 = 1 Порты на выход
	SETB	DDRD,6,R16	; DDRD.6 = 1
 
; Выставляем для обоих каналов ШИМ режим вывода ОС** выключеным. 
; COM1A = 00 и COM1B = 00
; Также ставим режим FAST PWM 8bit (таймер 16ти разрядный и допускает
; большую разрядность ШИМ сигнала. Вплоть до 10 бит.  WGM = 0101
; Осталось только запустить таймер на частоте МК CS = 001
 
	OUTI 	TCCR1A,0<<COM1A0|0<<COM1B0|0<<WGM11|1<<WGM10	 
	OUTI	TCCR1B,0<<WGM13|1<<WGM12|1<<CS10	
 
	SETB	TIMSK,OCIE1A,R16	; Включаем прерывание по сравнению А
	SETB	TIMSK,OCIE1B,R16	; Включаем прерывание по сравнению Б
	SETB	TIMSK,TOIE1,R16	; Включаем прерывание по переполнению Т1
					; Причем в режиме WGM=1010 переполнение
					; будет на FF т.е. таймер работает как
					; 8ми разрядный.

 
Осталось только прописать обработчики и вектора:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
         .CSEG
         .ORG $000		; (RESET) 
         RJMP   Reset
         .ORG $002
         RETI			; (INT0) External Interrupt Request 0
         .ORG $004
         RETI 			; (INT1) External Interrupt Request 1
         .ORG $006
         RETI			; (TIMER2 COMP) Timer/Counter2 Compare Match
         .ORG $008
         RETI 			; (TIMER2 OVF) Timer/Counter2 Overflow
         .ORG $00A
         RETI			; (TIMER1 CAPT) Timer/Counter1 Capture Event
         .ORG $00C 
         RJMP Timer1_OCA		; (TIMER1 COMPA) Timer/Counter1 Compare Match A
         .ORG $00E
         RJMP Timer1_OCB		; (TIMER1 COMPB) Timer/Counter1 Compare Match B
         .ORG $010
         RJMP Timer1_OVF 		; (TIMER1 OVF) Timer/Counter1 Overflow
         .ORG $012
         RJMP	Timer0_OV 	; (TIMER0 OVF) Timer/Counter0 Overflow
         .ORG $014
         RETI 			; (SPI,STC) Serial Transfer Complete
         .ORG $016
         RETI     			; (USART,RXC) USART, Rx Complete
         .ORG $018
         RETI			; (USART,UDRE) USART Data Register Empty
         .ORG $01A
         RETI			; (USART,TXC) USART, Tx Complete
         .ORG $01C
         RETI			; (ADC) ADC Conversion Complete
         .ORG $01E
         RETI			; (EE_RDY) EEPROM Ready
         .ORG $020
         RETI			; (ANA_COMP) Analog Comparator
         .ORG $022
         RETI			; (TWI) 2-wire Serial Interface
         .ORG $024
         RETI			; (INT2) External Interrupt Request 2
         .ORG $026
         RETI			; (TIMER0 COMP) Timer/Counter0 Compare Match
         .ORG $028
         RETI			; (SPM_RDY) Store Program Memory Ready
 
	 .ORG   INT_VECTORS_SIZE      	; Конец таблицы прерываний
 
; Interrupts ==============================================
Timer0_OV:	PUSHF
		PUSH	R17
		PUSH	R18
		PUSH	R19
 
		INCM	TCNT
 
		POP	R19
		POP	R18
		POP	R17
		POPF
 
		RETI
 
; Вот наши обработчики на ШИМ
Timer1_OCA:	SBI	PORTD,3
		RETI
 
Timer1_OCB:	SBI	PORTD,6
		RETI
 
Timer1_OVF: 	CBI	PORTD,3
		CBI	PORTD,6
		RETI
; End Interrupts ==========================================

Почему я в этих обработчиках не сохраняю регистры и SREG? А незачем! Команды SBI меняют только конкретные биты (а больше нам и не надо), не влияя на флаги и другие регистры.

 
Запустили…

 


 
И получили полную херню. Т.е. ШИМ как бы есть, но почему то адово мерцает. А на осциллографе в этот момент полный треш. Кто виноват? Видимо конфликт прерываний. Осталось только выяснить где именно. Сейчас я вам дам практический пример реалтаймовой отладки :)

 
Итак, что мы имеем:

 
ШИМ, как таковой, работает. Скважность меняется. Значит наш алгоритм верен.
Но длительности скачут. Почему? Видимо потому, что что-то мешает им встать вовремя. Когда у нас возникают фронты? Правильно — по прерываниям. А прерывания по таймерам. Т.е. врать не должны. Однако так получается. Давайте узнаем каком месте у нас конфликт.

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

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
29
30
31
32
33
; Interrupts ==============================================
Timer0_OV:	PUSHF
		PUSH	R17
		PUSH	R18
		PUSH	R19
 
		INCM	TCNT
 
		POP	R19
		POP	R18
		POP	R17
		POPF
 
		RETI
 
; Установка бита ШИМ канала А
Timer1_OCA:	SBI	PORTD,3
		RETI
 
; Установка бита ШИМ канала Б
Timer1_OCB:	SBI	PORTD,6
		RETI
 
;Сброс бита ШИМ канала А и Б
Timer1_OVF: 	CBI	PORTD,3
		CBI	PORTD,6
 
;DEBUG PIN BEGIN ---------------
		PUSHF
		INVBM	PORTD,7    
		POPF
;DEBUG PIN END -----------------
		RETI

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

 


 
Из картинки стало понятно, что у нас накрывается прерывание по сравнению. Давайте попробуем посмотреть с какими прерыванием происходит конфликт. Особых вариантов у нас нет — прерываний у нас тут четрые. А наиболее очевиден конфликт Timer0_OV vs Timer1_OCA vs Timer1_OCB.

 
OCA и OCB конфликтуют только тогда, когда счетные регистры у них сравниваются — вызов происходит почти одновременно, но сами обработчики короткие — всего несколько тактов, поэтому дребезг не столь сильный.

 
А вот Timer0_OV делает довольно мощный прогруз стека и еще вычитает четырехбайтную переменную. Т.е. тактов на 20 может задержать обработчик установки бита Timer1_OC* от того и вылазят такие зверские дребезги.

 
Давайте проверим эту идею. Разрешим прерывания в обработчике Timer0_0V

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
; Interrupts ==============================================
Timer0_OV:	SEI
		PUSHF
		PUSH	R17
		PUSH	R18
		PUSH	R19
 
		INCM	TCNT
 
		POP	R19
		POP	R18
		POP	R17
		POPF
 
		RETI
 
; Установка бита ШИМ канала А
Timer1_OCA:	SBI	PORTD,3
		RETI
 
; Установка бита ШИМ канала Б
Timer1_OCB:	SBI	PORTD,6
		RETI
 
;Сброс бита ШИМ канала А и Б
Timer1_OVF: 	CBI	PORTD,3
		CBI	PORTD,6
		RETI

 


 
Картина сразу исправилась. Теперь более важное (для нас важное) прерывание задвигает обработчик от Таймера 0. Но тут надо просекать возможные риски:

 

  • Более глубокий прогруз стека
  • Нарушается атомарный доступ к четырехбайтной переменной TCNT, поэтому если бы у нас было еще какое-то прерывание, меняющее TCNT то его надо было бы запрещать локально. Иначе бы мы получили такой трешняк, что проще заново прогу переписать, чем это отладить

.

 
ШИМ на таймерах
Когда совсем все плохо, то можно сделать на любом таймере. В обработчик прерывания по переполнению таймера заносим конечный автомат, который сначала загрузит в таймер длительность низкого уровня, а при следующем заходе — длительность высокого. Ну и, само собой, ноги процессора подергает как надо. Таким образом, на один таймер можно повесить дофига ШИМ каналов, но задолбаешься все с кодовой реализацией всего этого. И процессорное время жрать будет некисло. Не говоря уже про дребезги, о которых только что было сказано. Это для эстетов извращенцев :)))))

 
Исходник к статье

332 thoughts on “AVR. Учебный курс. Использование ШИМ”

  1. Ну на счёт дросселя не скажу =) У меня с индуктивностями отношения тяжелые =)
    А без резюка можно и вывод у ШИМ-генератора спалить. На выходе получается перманентная «КоЗа» =)

  2. У меня есть на tiny13 управление трёхцветным диодом. Так там 3 канала ШИМ реализованы совершенно тупо — в главном цикле программы. А вот уже управление. когда какую мощность на какой канал выдать, живёт по прерываниям от таймера.

  3. Замечательно пишешь, читается очень легко!

    Добавлю от себя: современные промышленные бесколлекторные асинхронные двигатели управляются обычно по принципу «двухполярной ШИМ», где каждый её полюс «склеивают» из пары Phase Correct PWM и соответственно пары ключей.

    «Phase Correct PWM» по-русски иногда называют «симметричной ШИМ»

  4. Здравствуйте, инициализовал вот таким образм, как написано у вас в статье:

    LDI R16,(1<<COM1A1)|(1<<COM1B1)|(1<<WGM10) ; Инициализация PWM
    OUT TCCR1A, R16
    LDI R16,(1<<WGM12)|(1<255,0->255
    В счётном регистре должно считаться от 0 до 255, потом сразу 0. А у меня считает от 0 до 255, потом до 0 в обратную сторону.
    Вообщем должен считать: 0->255,0->255
    А он считает : 0->255->0,0->255->0.
    Моделирую в AVR Studio.
    В чём может быть проблема? В неправильной инициализации или это AVR Studio не прпвильно работает?

    1. У тебя получился режим точной фазы. В нем считает так. Позырь в своем даташите по поводу режимов.

      Режим точной фазы нужен для создания многофазной системы ШИМ, чтобы когда меняешь скважность на разных каналах не происходил фазовый сдвиг (начала периодов) разных каналов между собой.

      В общем, разберись с битами WGM и выстави режим Fast PWM

      1. Я использую ATmega8
        Сверился с документацией,биты PWM13,PWM12,PWM11,PWM10
        0 1 0 1
        означают режим FAST PWM,8-bit
        Счётный регистр же не смотря на все эти доводы продолжает счиать от 0 до 255 и в обратном направлении… уже не знаю как убедить его рабоать как надо… проверял осциллографом на OC1A и OC1B никаких импульсов нету(

        1. скинь мне проект, погляжу. А то код инициализации что ты мне запостил какой то побитый получился. Сам погляди.

          или запости сюда код инициализации его как есть, копипастом, окружив в теги:
          <pre lang=»AVR» line=»1″></pre>

          ХА! Да, ты прав. АВР студия глючит нипадецки! Это уже второй ее глюк на Мега8 Гыгыгы. У меня тоже считает в режиме точной фаз, хотя на самом деле работает все ок.


          Это два канала моих ОС1В и ОС1А на одном порог стоит в 10 на другом в 100. Режим Быстрый ШИМ. Будь он Точной фазой центры импульсов бы совпадали. Значит работает верно — в студии же глючит. Инициализация кода вот такая:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          
          	SBI		DDRB,2
          	SBI		DDRB,1
          	; Не забудь выставить направление порта!!!
           
          	OUTI	TCCR1A,(1<<COM1A1)|(1<<COM1B1)|(1<<WGM10)	; Инициализация PWM
          	OUTI	TCCR1B,(1<<WGM12)|(1<<CS10)	; Режим Fast PWM
           
          	OUTI	OCR1AL,100
          	OUTI	OCR1BL,10
          1. Ах это проклятая AVR Studio значит)
            А вот про направление портов я и забыл!… вот причина моих бед.
            Спасибо за оперативные ответы, на осциллографе всё полный порядок))

  5. «Когда значение в счётном регистре таймера достигнет значения находящегося в счетном регистре, то могут возникнуть следующие аппаратные события.»
    Масло маслянное — чета тут видимо очепятка. Наверно достигнет значения одного из регистров сравнения

    «Можно повысить разрешение, сделав счет 8, 9, 10 разрядным, но надо учитывать, что повышение разрядности, вместе с повышением дискретности выходного аналогового сигнала, резко снижает частоту ШИМ.»
    Не всосал чет. Вроде счет и так 8-разрядный (делишь частоту на 256). И не понял почему резко снизится частота ШИМ при увеличении разрядности. Я так понял на каждый бит увеличения — частота снижается вдвое. да?

    Еще в таблице COMxx1:COMxx0 вторая строка — там написано Иначе не отцеплен, тогда смысл непонятен. Может всетаки Иначе ОН отцеплен ?

    А как вы на осциллографе смотрите в студии? чет не нашел я его.

    1. Ну так на 8ми разрядном делишь частоту на 256, на 9ти разрядном на 512, а на 10 делить придется уже на 1024.

      В студии нет осциллографа. А это просто скриншоты с моего осциллографа.

  6. Доброго времени суток! Может вопрос не в тему, но задам таки. В проекте в PPOTEUS мне нужно использовать ATmega8 вместе с SG3525. Но, если ATmega8 есть в библиотеке PROTEUS, то SG3525 нет. Может кто подскажет где взять библиотечку с SG3525 для PROTEUS. А то вконец работа остановилась! SG3525 в проекте управляет полевиками, работающими на плечи первичной обмотки со средней точкой импульсного трансформатора. Буду очень благодарен. С наступающим праздником Днем защитника Отечества!

  7. Частота получившегося ШИМ сигнала определяется просто: Частота процесора 8МГц, таймер Т/С1 тикает до 256 с тактовой частотой. Значит один период ШИМ будет равен 8000 000/256 = 31250Гц. Вполне недурно.
    Быстрей не получится — это максимальная скорость на внутреннем 8МГц тактовом генераторе. Можно повысить разрешение, сделав счет 8, 9, 10 разрядным, но надо учитывать, что повышение разрядности, вместе с повышением дискретности выходного аналогового сигнала, резко снижает частоту ШИМ.
    ———
    есть задача в противоположном.. как понизить разрядность? (7[127],6[64])
    тем самым повысить частоту ШИМ в 2,4 раз соответственно..
    есть ндобоство так сказать

      1. согласен сам думал что програмно.. но сказано что пробегает значения от 0(=1) до 255 .допустим у нас 127(=0).. и как тут програмно немогу понять, ведь 1 раз выставляется 0 и 1 за цикл от0до255

        хотябы 7 разрядноять расмотрим

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

          1. расмотрим 7 разрядность 0..127
            тоесть задали мы число 55
            1(начало)1111….1110(это наше число 55 — мы до него дошли)0000…….0000(стоп — мы дошли до 127) — получили инфу что счетчик дошел до 127 и прерыванием сбрасываем(загружаем новый счет). и т.д.
            ?

            1. Нет, допустим граница у нас 64
              А надо получить ШИМ 50%

              Считаем до 64-50%=32 по прерыванию меняем сигнал на ноге, закладываем новый cчет равный 64-(64-50%)=32 и считаем опять до прерывания. ГДе все повторяется

  8. Подскажите как правильно рассчитать номиналы для интегрирующей цепочки на выходе?
    например у меня ATTiny2313 кварц на 20 Mhz. Режим ШИМ — быстрый. Таймер 8 бит. Частота ШИМ получается 78125 hz. Интересует сам алгоритм чтобы я мог его применить и для других частот ШИМ.

    1. У RC цепочки есть параметр — постоянная времени T. Это время за которое заряд изменится втрое. Соответственно Т должно быть таким, чтобы конденсатор не успевал перезаряжаться, то есть два три периода ШИМ сигнала.
      Т=R*C

  9. DI HALT, еще раз спасибо за хороший сайт!
    Сегодня собрал макетку COM->МК->двигатель (MAX232->Mega8->L293D).
    Воткнул одним концом в комп, другим — в отобранную у ребенка машинку.
    Работает, однако! =)

    Вопрос про ШИМ:
    При моей схеме куда пихать RC цепочку (и нужна ли она здесь вообще)?

      1. Ага, спасибо.

        Пожалуйста посмотри, правильно ли я понял:
        RC-цепочка нужна, когда инерционности самой нагрузки не хватает? Например для лампы. И этом случае она втыкается между последним цифровым компонентом (с которого идет ШИМ) и аналоговой частью.
        Например, в моем случае ШИМ-сигнал формируется контроллером, но ровно в такой же форме он проходит через L293D. Т.о. L293D — последний цифровой компонент. А вот дальше уже должен быть аналоговый сигнал, соответственно если и нужна RC-цепочка, то сразу после L293D.
        Так?

        1. Да понял правильно. RC образует фильтр который из пульсирующего вычленяет переменную составляющую. Остается только переменка.

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

          С лампой, кстати, RC тоже не нужна — нить накала обладает большой температурной инерцией.

          А вот подавать на вход ОУ в качестве опорного сигнала напрямую с ШИМ нельзя — надо фильтровать.

          1. Ок, понял. Спасибо.

            ШИМ, как ни странно, тоже заработал почти сразу (забыл сначала инициализацию порта на выход вставить). Теперь руки чешутся плату развести, чтобы установить это все богатство на шасси.
            В этой связи вопрос:
            Какой-нибудь статьи про разводку плат не планируется? ;-)
            Уже при трех микросхемах как-то все не просто получается, а ведь туда еще питание с землей надо, и подключения наружу должны торчать. Макетка напоминает то ли блюдо со спагетти то ли прическу жены утром =)

  10. Второй день перечитываю эту_статью-статью_с_gaw.ru-даташит. Никак не могу достьчь просветления =) Голова не соображает что-то..
    Работаю в CodeVision.
    // Выставляем порты

    PORTB=0xFF; // порт
    DDRB=0xFF; // на выход

    PORTD=0xFF; // порт
    DDRD=0x00; // на вход

    Далее нужно сконфигурировать таймер.
    TCCR1A=??; //
    TCCR1B=??; //

    И в нужный нам момент кинуть в регистр сравнения число.

    Есть несколько вопросов:
    1.) Распишите побитово TCCR1A, TCCR1B, пожалуйста
    2.) Число можно кидать в любое время в любом месте? Или нет? Можно например в главном цикле написать OCR1A=PORTD? Изменяя переключателями значение PORTD изменяем скважность.
    3.) После инициализации и заброса значений ШИМ работает автоматом? Т.е. надо ли что-нибудь подправлять, закидывать снова?

    PS: не думал, что так запутанно будет.. Привык на VB писать, развратил он меня в край =)

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

      Число можно закидывать в регистр сравнения когда угодно.

      1. с TCCR1A я примерно разобрался. На Гав.ру http://www.gaw.ru/html.cgi/txt/doc/micros/avr/arh/mega103_39.htm. TCCR1B выглядит не так. Там нет бит WGM13 WGM12, да и, кстати в TCCR1A нет WGM11 WGM10. А в той доке на Гав вообще как-то запутанно: есть ШИМ режим, и не ШИМ режим. И как переходить на ШИМ режим не написано. В принципе я твой код понял, но у меня нет значений констант COM1A1, COM1B1, WGM10, WGM12, CS10.

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

    1. Эмм а в чем проблема? Подаешь управляющие импульсы на ключи со смещением и все. Или ты что имеешь ввиду?

      Можно сделать на двух таймерах. Т.е. первый работает в ШИМ режиме, но ОС отключен от ноги МК — нам нужны только два его прерывания OCF и OVF по этим прерываниям мы запускаем второй таймер который и отсчитает нам дедтайм, а потом переключит ногу в своем прерывании.

      Либо конечный автомат на одном таймере. С четырьмя состояниями. Вверх, дедтайм, вниз, дедтайм.

      1. Вроде сообразил. Мне нужно было на одном таймере, сделать два шима, скважность регулируется с помощью ацп. Сделал так: в прерывании от ацп к регистру OCR1A прибавил константу (половина dead time), а от OCR1B отнял ту же константу, вроде работает.

  12. Сделал шим на 3 светодиода на таймере 1 и таймере 2. Когда ставлю в OCRxx 0, светодиоды притухают, но не гаснут полностью.
    Попробовал менять значения от 0 до 255, яркость плавно нарастает, потом светодиоды притухают, но опять же не гаснут полностью.
    В чём может быть косяк?

      1. Вот код. Осцилографа пока нет, чтобы проверить.
        ;Init PWM timers
        ;Timer 1
        ldi OTEMP,$A1
        out TCCR1A,OTEMP ;8-bit Fast PWM C(t)=CLK/1
        ldi OTEMP,$09
        out TCCR1B,OTEMP
        ldi OTEMP,65
        out OCR1AL,OTEMP ;
        ldi OTEMP,140
        out OCR1BL,OTEMP ;
        ;Timer 2
        ldi OTEMP,$69 ;8-bit Fast PWM C(t)=CLK/1
        out TCCR2,OTEMP
        ldi OTEMP,200
        out OCR2,OTEMP ;Duty Cycle = 0%

      2. Вот код. Осцилографа пока нет, чтобы проверить.
        ;Init PWM timers
        ;Timer 1
        ldi OTEMP,$A1
        out TCCR1A,OTEMP ;8-bit Fast PWM C(t)=CLK/1
        ldi OTEMP,$09
        out TCCR1B,OTEMP
        ldi OTEMP,65
        out OCR1AL,OTEMP ;
        ldi OTEMP,140
        out OCR1BL,OTEMP ;
        ;Timer 2
        ldi OTEMP,$69 ;8-bit Fast PWM C(t)=CLK/1
        out TCCR2,OTEMP
        ldi OTEMP,200
        out OCR2,OTEMP ;

          1. DI HALT, спасибо… действительно всё верно. Светодиоды такие чувствительные попались, заразы. Если двумя руками взяться за выводы, они даже так начинают слабенько светить. Другие светодиоды гаснут нормально :)

  13. могу ли я сделать цифровой процессор (преобразователь «аналог-ШИМ») на AtMega8 или на ATtiny85 с целью подать потом полученный сигнал на усилитель D-класса работающий в ключевом режиме?. если да то какой из них наиболее лучше подойдет? и еще, можно ли получить несущую частоту (шим)150-400 кГц аппаратно или программно?

    1. Ммм… частоты не шуточные. АВРКА может и не успеть. Считай максимальная частота шим с 8ми разрядным ШИМ у ней будет как тактовая /8 тактовая предельная (для некоторых АВР) 20мгц так что максимум ты из нее выжмешь 250кГц. попробовать можно.

  14. Подскажите как расчитывать резистор RC фильтра на выходе ШИМ после силовых мосфетов. Конденсатор я расчитал по формуле емкостного сопротивления. Резистор должен быть расчитан относительно максимально допустимого тока силовых мосфетов?

      1. В общем я так и сам думал что он не нужен, есть реально работающая схема без резистора, ток нагрузки 8А транзисторы на 50А пиковой. Очень интересует необходимость ставить кондер параллельно индуктивной нагрузке(двигатель постоянного тока) Вот приблизительная схема того что у нас будет: http://infarct.nm.ru/Files/U102Hbridge.pdf Мне просто кажется что в движке будут жуткие индуктивные потери, кондер собираюсь ставить на 20 микрофарад пленочный или бумажный. Мост будет использоваться на симуляторе автомобиля, собранного на разогнанном микроконтроллере и дополнительном PC с управлением через COM порт. Вот ссылка http://mal4x.org.ru/forum/topic.php?forum=2&topic=51&v=|#1270560902
        В дальнейшем планируем разработать свою прошивку, так как оригинальная нас многим не устраивает, в частности своей глючностью. Исходники автор не выкладывает, но как мне кажется вполне реально самому разобраться и сделать ШИМ контроллер с обратной связью через датчик Холла или потенциометр.

              1. А как же быть с индуктивными потерями на движке? У нас частота шим модуляции 24 КГц, он хоть и крутится, но ощутимо греется, хотелось бы чтобы места тепла было побольше КПД выраженного в механической энергии. Может там все таки поставить кондер, но небольшой емкости?

                1. Получишь бросок тока на ключ и емкостные потери. Если ставить, то LC фильтр, чтобы L сгладила бросок тока через кондер. Но потеряешь в динамичности привода. Он станет более ленивым чтоль. А греться он будет и так и так.

  15. Здравствуйте! Пытаюсь сгенерировать синусоиду ШИМом на ATmega16u4. Кварц стоит 16 МГц. Но на осцилографе упорно выходит синусоида 120 Гц :(
    #include
    #include
    #include
    #include

    void hwInit(void);
    unsigned char tmp = 0x00;
    unsigned char sin_tab[] = {0,2,5,10,16,22,30,39,48,59,69,81,93,105,117,131,143,
    155,165,179,190,201,210,220,228,235,241,247,251,253,254,
    254,254,252,249,244,238,232,224,215,206,195,185,173,161,
    149,137,124,111,99,87,75,64,53,44,34,26,19,13,7,3,1,0,0};

    void main(void)
    {
    hwInit();

    sei();
    while (1);
    }

    void hwInit()
    {
    DDRB = (1<<DDB3);

    // Fast PWM
    TCCR1A = (0<<COM1A1)|(0<<COM1A0)|(0<<WGM11)|(1<<WGM10);
    TCCR1B = (1<<WGM12)|(1<<CS10);
    TIMSK1 = (1<<TOIE1)|(1<<OCIE1A);

    // Timer 0 Overflow CTC
    TCNT0 = 0;
    TCCR0A = (1<<WGM01)|(1<<COM0A0);
    TCCR0B = (1<<CS00);
    TIMSK0 = 1<<TOIE0;
    OCR0A = 0xFF;
    }

    ISR(TIMER1_COMPA_vect)
    {
    PORTB = (1<<PORTB3);
    }

    ISR(TIMER1_OVF_vect)
    {
    PORTB = (0<<PORTB3);
    }

    ISR(TIMER0_OVF_vect)
    {
    OCR1A = sin_tab[tmp++];
    if(tmp == 64)
    tmp = 0;
    }

    На выходе стоит ФНЧ.
    Подскажите, как можно изменять её частоту в пределах от 120 Гц до 3 кГц. Спасибо!

  16. всем доброго дня! и всего лишь этого куска хватит для генерации шим на борту?

    ;FastPWM Init
    SETB DDRD,4,R16 ; DDRD.4 = 1 Порты на выход
    SETB DDRD,5,R16 ; DDRD.5 = 1

    ; Выставляем для обоих каналов ШИМ режим вывода ОС** сброс при совпадении.
    ; COM1A = 10 и COM1B = 10
    ; Также ставим режим FAST PWM 8bit (таймер 16ти разрядный и допускает
    ; большую разрядность ШИМ сигнала. Вплоть до 10 бит. WGM = 0101
    ; Осталось только запустить таймер на частоте МК CS = 001

    OUTI TCCR1A,2<<COM1A0|2<<COM1B0|0<<WGM11|1<<WGM10
    OUTI TCCR1B,0<<WGM13|1<<WGM12|1<<CS10

    1. Да. Ведь шим у нас генерирует периферийный генератор на базе таймера. А значит нам надо запустить таймер нужным образом и все.

      Ну а потом складывать данные в регистры сравнения OCR, получая нужную скважность.

    1. В исходнике, что в архиве для шима малая часть. Остальное это макросы, инициализация контроллера, организация главного цикла. Об этом в предыдущих частях курса по ассемблеру авр. Там последовательно это все накручивается.

  17. Di, во-первых, громадное тебе спасибо за данный великолепный сайт! Кладезь инфы предельно доступным и живым языком ) он (точнее, ты )) явился моим проводником в мир микроконтроллеров. РЕСПЕКТИЩЕ!!!!

    и маленький вопрос вдогонку ) есть такая вещь — ATtiny25. Там 4 канала АЦП и 4 выхода ШИМ. Но — очень мало ног на DIP. Вопрос — а их (все АЦП и ШИМ) как-нибудь можно заюзать одновременно? Заранее спасибо!

    1. А они пересекаются? Если да, то нет. Т.к. ШИМ это выход, а АЦП это вход. И тебе придется выбрать что будет входом, а что выходом. В принципе, можно попробвать сделать схематический изврат. Поставить диоды снаружи и в некоторые моменты юзать как вход, а в некоторые как выход, а диод защитит датчик (или что там у тебя подает сигнал на вход ацп) от напряжение которое выйдет с шима. Но точность ацп от этого сильно пострадает да и вообще изврат еще тот. Тебе придется периодически выключать шим и мерить сигнал на ацп.

  18. Доброго времени суток. Столкнулся со следующей проблемой: залил в контроллер (ATTiny2313 тактируется внешнем кварцем на 1МГц, согласовывающие конденсаторы на 18пФ) обычную программу-мигалку (частота 0.5Гц). Всё отлично работает, только иногда при включении частота увеличивается примерно в 3-4 раза, а потом через некоторое время (секунд 10) резко приходит в норму. Эффект такого увеличения частоты также наблюдался после долгой работы устройства (часов 8) и лечился только перезагрузкой. В момент аномалии на ноге XTAL2 контроллера вместо обычной синусоиды сложный нестабильный сигнал похожий на сумму синусоид с частотами 1МГц и 2МГц, в то время как на ноге XTAL1 – ровная синусоида 1МГц! Земля вроде разведена нормально, плата тщательно была промыта в УЗ-ванне и покрыта полиуретановым лаком. Вопрос в следующем: что может сбоить – китайский кварц, китайские конденсаторы или у Tiny бывают такие проблемы?

  19. как можно синхронно запустить тикать таймер_0 (portB.3) и таймер_1 (portD.4 / portD.5) ?

    [code]
    LDI r16,(1<<PSR10)|(1<<PSR2)
    OUT SFIOR,r16
    [\code]

    непомогает, первый стартует тот у кого раньше происходит запись в TCCR_ХХХ_

    1. Есть в SFIOR бит TSM — остановка всех предделителей. Пишешь туда 1 — предделитель блокируется, таймеры встают. Инициализируешь их как надо, запускаешь. Записываешь в TSM 0 и все пошли синхронно.

          1. Хм… слушай чето тоже найти не могу. Есть костыльное решение. Например записать в таймер значение со смещением в один-два такта, чтобы у того кто стартанет вторым была фора на его инициализацию.

        1. прескалер = 8 -> это значит за 8тактов процесора (8МГц), счетчик тикнет 1 раз?

          (8000000/8) / 256 = 3906,25 Гц частота ШИМ (при минимальном делителе)

          а нужно 20кГЦ… без делителя много с делителем маловато будет…

          или я недогоняю как им пользавоться?

            1. по условию сказано ШИМ до 20кГц… можно меньше.

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

      1. извеняюсь, но я тока учусь

        по тексту:
        «Частота получившегося ШИМ сигнала определяется просто: Частота процесора 8Мгц, таймер тикает до 256 с тактовой частотой. Значит один период ШИМ будет равен 8000 000/256 = 31250Гц. »

        далее идет описание режима СТС… и там:

        «Например, надо нам прерывание каждую миллисекунду…Пусть у нас частота 8Мгц.

        Прескалер будет равен 64, таким образом, частота тиков таймера составит 125000 Гц. А нам надо прерывание с частотой 1000Гц. Поэтому настраиваем прерывание по совпадению с числом 125…»

        Как тут получилось 125000?

  20. «то получим самое обычное трехфазное напряжение, а значит привет бесколлекторные асинхронные и синхронные двигатели»

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

    1. про биты COM1A1:COM1A0 и COM1B1:COM1B0 и
      WGM11 и WGM10 вместе с битами WGM12 и WGM13 :

      Жили-были три японца — Як, Як-Цедрак, Як-Цедрак-Цедрак-Цедрони.
      Жили-были три японки — Цыпа, Цыпа-Дрипа, Цыпа-Дрипа- Дримпопони.
      Все они переженились — Як на Цыпе, Як-Цедрак на Цыпе-Дрипе,
      Як-Цедрак-Цедрак-Цедрони на Цыпе-Дрипе- Дримпопони.
      И родились у них детки — У Яка с Цыпой — Шах, у Як-Цедрака с Цыпой-Дрипой — Шах-Шарах,
      у Як-Цедрак-Цедрак-Цедрони с Цыпой-Дрипой- Дримпопони —
      Шах-Шарах-Шарах-Шарони. :-))) хеппи хелоуин!

    2. Это макросы относятся к организации программы вообще. Алгоритм шарманки, чтобы организовать временное разделение задач не используя никаких аппаратных ресурсов вообще. Показана для примера, т.к. в использовании не очень удобна и применяется редко. Но рулит если все таймеры надо выделить в дело (например, для генерации многих ШИМ Одновременно по всем таймерам). В конце статьи архив с исходниками примера и там все есть.

      1. Спасибо. слона то я и не заметил(т.е. прикрепленный файл)
        Просто хотел глянуть, как протеус будет реагировать на конфликт с запретом вложеного прерывания. реального осцила к сожалению нет.
        Посмотрел, дребезг можно заметить, хоть и не так очевидно как на вашем настоящем, но хоть буду знать как выглядит.
        З.Ы. В коде прикрепленного файла и других местах наверное ачипятка? :
        OUTI TCCR1A,0<<COM1A0|0<<COM1B0|0<<WGM10|1<<WGM10 а надо
        OUTI TCCR1A,0<<COM1A0|0<<COM1B0|0<<WGM11|1<<WGM10
        думаю не критично -ведь бит WGM11 все равно равен 0.
        Может биты ноликов вообще не обязательно выставлять ???

        Начал изучать ASM по вашим урокам с месяц назад, очень доволен этим сайтом, огромное спасибо! С первой «получки» от контроллеров выставлюсь :-)

    1. Разрядностью :)

      У 8ми разрядного 256 градаций скважности
      у 9ти разрядного 512 у 10ти 1024.

      Ну и, соответственно, скоростью (т.е. предельной частотой). 8ми разрядный шим прогоняет период за 256 тиков таймера, а 10и за 1024.

  21. цит: прямой ШИМ (сброс при совпадении и установка при обнулении счета)
    простите,а установка чего? судя по графику какраз выход устанавливается в логическую 1 при совпадении со счетным регистром… или я не понимаю :(

    ещё непонятный момент — мы устанавливаем !!!биты!!! СОМ1А1:СОМ1А0 =0b10 следующим образом: (2<<COM1A0)? т.е. 0 остается в COM1A0 а 1 переходит в СОМ1А1 чето запутано…

    вопрос о Phase Correct PWM.
    цит: Основное его предназначение, делать многофазные ШИМ сигналы, например, трехфазную синусоиду. Чтобы при изменении скважности не сбивался угол фазового сдвига между двумя ШИМ сигналами. Т.е. центры импульсов в разных каналах и на разной скважности будут совпадать.
    А если центры импульсов не будут совпадать, разве это сильно повлияет на угол фазового сдвига? Сигнал то все-равно интегрируется…

    не могли бы вы добавить пример с изменением частоты ШИМ или дать ссылку.
    Заранее спасибо тем добрым людям кто захочет ответить :-Р

    1. Устновка и сброс бита на выводе контроллера. А график сделан для какого то одного из режимов.

      тут мы значение 10(2) сдвигаем на место с начлом в COM1A0. т.к. COM1A1:COM1A0 то получится, что COM1A1 =1, а COM1A0 = 0. Чем тормозить, лучше бы прогнал в эмуляторе и увидел сам что происходит. Изучать надо с компилятором под рукой, а не просто по тексту :)

      Если у одного канала значение около 0, а у другого в районе 255 то фазы уплывут очень сильно. И толку что оно интегрируется? Возникнет куча паразитных вещей, искажение формы поля машины, лишние потери. Оно нам надо?

      В конце статьи прикреплен исходник примера где два канала ШИМ меняются.

  22. Уважаемый автор, спасибо огромное за создание и эксплуатацию этого сайта!
    Если будет время то.. (Буду краток) —

    Как проверить работоспособность МК(программатор Громова собрал, аттини2313v купил, аврСтудию поставил, в эмуляторе все работает-шим, в юнипроге все записывается — читается, в реале на ноге не появляется ни импульса ни лог1) — может я что-то пропустил?

    Заранее спасибо)

  23. Доброго времени суток, уважаемай комьюнити!
    прошу посоветовать, как лучше реализовать амбилайт для ПК (была похожая статья на хабре, но там для 4 светодиодов, да и мерцания присутствуют).
    Планирую использовать 6 RGB светодиодов (2 справа, 2 слева, 2 сверху), необходимо иметь возможность плавно менять яркость каждого из них (то есть 6х3= 18 ШИМ каналов). 18 аппаратных ШИМ каналов я не видел ни на одном МК АВР (допускаю что плохо искал), следовательно прийдется все каналы реализовывать програмно.
    Будет ли работать следующая схема?
    На выводы МК вешаю 3 сдвиговых 8битных регистра (3 вывода под data на каждый из регистров, и 1 вывод на тактовые входы сдвиговых регистров). Далее с выводов сдвиговых регистров через RC цепочку на светодиоды.

    Данные цветности буду передавать в МК через usb.

    Если схема работать будет, то:
    — какой частоты должен быть ШИМ сигнал для светодиодов, чтобы глаз не замечал подвоха?

    А может быть лучше использовать аппаратные драйверы светодиодов?
    Посоветуйте, как лучше сделать?
    Спасибо.

        1. ARM Cortex M3 например. Но не суть. От частоты все зависит. Можно и программно, на конечном автомате сделать.

          Если девайс штучный, то я бы не парился. Сварганил все на нескольких МК. Например Тини2313 может организовать 4 аппаратных ШИМ канала. Стоит недорого (около 50р). Пять тинек одна совмещенная с управлением всем этим борделем даст искомый результат. Тогда программная реализацию будет проще простого.

  24. Прошу прощения, если уже обсуждалось. можно повысить частоту ШИМа, выбрав в битах WGM13:10 режим, когда топ не фиксированный 8, 9 или 10 бит, а задается регистром ICRx. К примеру, используя внутренние 8МГц и задав ТОП = 100 вместо 255 для 8-битного мы повысим максималльную частоту в 2.5 раза — до 80КГц
    Проверил на практике — всё работает и даже вроде как надо, но осциллограф сказал бы точно :)

  25. Хочу копировать значение регистра ADCH в регистр OCR2. Необходимо регулировать
    скважность ШИМ входным аналогом. Пытаюсь через MOV. Студия показывает ошибку.
    Подскажите пожалуста в чем дело и как можно по другому?

    1. ПОтому что так нельзя. Сначало надо из ADCH через IN взять в R какой нибудь (в R16 например), а потом оттуда через OUT выгрузить в OCR2

      Это тебе не х86 тут нельзя просто так гонять данные между памятью.

  26. SETB
    При настройке портов на выход можно заменить на
    SBI DDRB, 4
    SBI DDRB, 5

    А в следующем отрывке он уже используется для другого
    SETB TIMSK,OCIE1A,R16 ; Включаем прерывание по сравнению А
    SETB TIMSK,OCIE1B,R16 ; Включаем прерывание по сравнению Б
    SETB TIMSK,TOIE1,R16 ; Включаем прерывание по переполнению Т1

    Посмотрел исходник, там вообще шестнадцатеричным кодом всё написано)))

  27. Нужно организовать на меге 8 воспроизведение заданой формы сигнала, например синусоиды, форма сигнала зашивается значениями амплитуды в массиве. Частота и амплитуда сигнала задается с клавы.
    Идея такая: первый таймер делает быстрый шим, второй делает прерывания по сравнению через промежутки времени равные частоте, заданой в начале, деленной на количество точек в массиве, а в прерывании меняет регистр сравнения первого и заодно свой, чтобы прерваться в следующий раз через нужный промежуток времени.
    Оцените идею =) Тут меня еще мучает вопрос будет ли прерываться работа шим первого таймера во время прерывания второго?

  28. Я прошу прощения за возможно не лепый вопрос
    Подскажи пожалуйста, какой резистор и кондер ставить на RC фильтр и если можно дайте ссылки на их расчеты?
    Да и еще, т.к. кондер от своего заряженного состояния может замыкать на землю ножку, не спалится так контролер? т.е. можно прям напрямую подключать кондер, ножка и земля?
    Кондер электролит?

            1. На выходе с транзистора там будут мощные ШИМ импульсы.

              А дальше от нагрузки зависит. Если нагрузка индуктивная, то их сгладит за счет индуктивности нагрузки. Если это нагреватель или осветитель, то там пофигу — сгладит тепловая инерция системы.

  29. Вот тут, по-моему ошибка

    [quote]
    ШИМ на прерываниях.
    Но вот засада — плата уже разведена, захотелось ШИМ, а выводы OCxx уже задействованы под другие цели.
    …….
    Пример:1

    ; ШИМ будет на выводах 3 и 6 порта D
    SETB DDRD,3,R16 ; DDRD.3 = 1 Порты на выход
    SETB DDRD,6,R16 ; DDRD.6 = 1

    …………….

    OUTI TCCR1A,0<<COM1A0|0<<COM1B0|[b]0<<WGM10[/b]|1<<WGM10
    OUTI TCCR1B,0<<WGM13|1<<WGM12|1<<CS10
    [/quote]

    Два раза прописан WGM10.

  30. Спасибо большое за статью.
    Получилось самому поиграть шимом и даже управлять драйвером для мощных светодиодов.
    Вопрос вот у меня в чём. На днях увидел красивое видео
    http://www.youtube.com/watch?v=V5hWZvli9as&feature=player_embedded
    на атиньке можно, судя по всему 4 канала ШИМ использовать, а на этом видео, судя по всему 12 каналов работаю. Каким же образом?

  31. вчера заметил ещё один глюк в Студии
    в устройстве требовалось организовать простейший индиактор — чтобы светодиод периодически подмигивал — мол, живой я
    без лишнего софта приспособил под это дело Timer1 в режиме Fast PWM просто загрузкой OCR1A и ICR1A
    Студия категорически отказалась показывать реальную картину, хотя в железе — «картина маслом»
    так что прежде чем биться лбом об стену — проверяйте в железе

  32. Автору огромное спасибо, очень подробно и доходчиво всё разжевано:)
    я только недавно начал изучать AVR,
    есть одно замечание- косяк скорее мой в том что не заметил сразу «Исходник к статье», а в нем описаны макросы, которые применялись, в статье толком об этом не говорится, скачал, увидел как всё это работает, теперь доволен как таракан :-D

  33. Подскажите:
    на вход компаратора подаю синус 50Гц и пилу 20кГц. Как влияет разница амплитуд синуса и пилы на входе компаратора на характер сигнала на выходе компаратора?

  34. Доброго времени суток! Стоит задача задавать управляющее напряжение для ГУН’а посредством ШИМ и интегрирующей RC-цепочки. В железе все давно собрано, сейчас дошел до программной реализации ШИМ и столкнулся с проблемой. Чтобы после RC-цепочки (10КОм, 10мкФ) не было пульсаций требуется частота выше 100КГц (если верить Proteus то идеално под 200КГц), а это у меня не получается. Аппаратно можно сделать только ~37КГц. Пробывал реализовать программно с большей частотой, но не получилось.
    Может кто знает как можно решить эту проблему?
    Заранее благодарен.

    1. Вариантов тут немного. Можно понизить разрядность ШИМ’а и тогда он будет щелкать быстрей. Скажем сделать его 5ти разрядным ,ограничив TOP в режиме FastPWM

      Можно взять другой МК. Например Tiny45 имеет встроенный PLL позволяющий разгонять тактование таймеров до 40 чтоль МГЦ. Соответственно получить тут 200кГц не должно стать проблемой.

  35. Странно что-то у меня с ATTiny25.
    Режимы — COM1B
    0 0 — отключен от пина
    0 1 — при совпадении с OC1B — пин меняет состояние на противоположное (т.е. H/L 50/50)
    1 0
    1 1 — шима вообще нет (один раз пин переключается в 1 или 0 и все)
    Изучил даташит — в непонятках. Начал научным тыком — нашел. Надо ставить в единицу бит PWM1B в GTCCR. Очень логично, конечно, если бы не описание
    «Bit 6 – PWM1B: Pulse Width Modulator B Enable
    When set (one) this bit enables PWM mode based on comparator OCR1B in Timer/Counter1
    and the counter value is reset to $00 in the CPU clock cycle after a compare match with OCR1C
    register value.» Т.е. при чем тут OCR1C — не понятно. И почему когда этот бит выставил, и выставил COM в 0 1 — заработал нормальный ШИМ (до OCR еденица, после — ноль)

    1. Ну так все верно.

      Ты включаешь битом PWM1B модулятор. А ШИМ формируется путем сравнения значения в TCNT с значением в OCRxx меняя значение в OCR1B мы задаем скважность.

      А OCR1C задает максимальное значение счета. Выше было сказано:

      » OCR1C holds the Timer/Counter maximum value, i.e. the clear on compare match value. In the normal mode an overflow interrupt (TOV1) is generated when Timer/Counter1 counts from $FF to $00, while in the PWM mode the overflow interrupt is generated when Timer/Counter1 counts either from $FF to $00 or from OCR1C to $00. The inverted PWM outputs OC1A and OC1B are not connected in normal mode.»

      1. «When set (one) this bit enables PWM mode based on comparator OCR1B in Timer/Counter1 and…» дальше нужно было не читать. Сбило с толку)
        Спасибо за пояснение. Каждый раз удивляюсь, как много внимания Вы уделяете порталу. Еще раз спасибо!

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

    1. Ага. Т.е. надо тебе 1гц — пробегаешь по таблице за 1секунду. Надо тебе 100гц — 100 раз в секунду. И так пока не упрешься либо в быстродействие МК (на выборку уйдет не меньше трех пяти тактов). Либо в частоту ШИМа которая не будет успевать перестроиться.

  36. Здравствуйте! Хочу сказать Вам огромное спасибо за огромное количество (и качество!!) информации на Вашем сайте.
    А еще возник такой вопрос — мне нужно, чтобы значение в OCR менялось в зависимости от значения , принимаемого АЦП. Ну т.е есть переменный резистор, мы его покрутили, АЦП обработал, и в зависимости от этого значения поменялся порог сравнения (ширина импульсов). Что во что запихивать в таком случае? Буду оооочень признательна за совет!!

    1. В прерывании по окончанию замера ацп сделать внесение нужного значения в OCR вот и все. Если надо напрямую, то OCR = ADC. Ну или зависимость какую прописать

      1. спасибо за оперативный ответ, мне почему-то казалось , что нельзя прям из АЦП кидать в ОСR , но научного обоснования этому нет. буду пытаться написать код!
        и еще вопрос — я возможно пропустила какие-то начальные уроки введения в курс, где можно прочитать пояснение такого типа записи :
        OUTI TCCR1A,0<<COM1A0|0<<COM1B0|0<<WGM11|1<<WGM10
        OUTI TCCR1B,0<<WGM13|1<<WGM12|1<<CS10

        1. Ну напрямую нельзя. Т.е.

          сначала IN Rxx , ADCH
          потом OUT OCR, Rxx

          Т.е. через регистр. Также учитыввай тот факт что OCR может быть как 16ти так и 8ми разрядным, в зависимости от разрядности таймера, а результат АЦП может быть как 8ми (выравнивание влево, младшие биты не читаем, берем из ADCH) так и 10 разрядным из двух байт ADCH и ADCL.

          Это просто макрос. OUTI
          раскрывается в

          LDI R16,0<<COM1A0|0<<COM1B0|0<<WGM11|1<<WGM10
          OUT TCCR1A,R16

          Сам макрос у меня описан где то в исходниках в файле macro.inc или что то подобное.

  37. Уже не первый раз сталкиваюсь с такой вещью: при работе счётчиков в режиме FastPWM, выход подключен «Clear OC2 on Compare Match, set OC2 at BOTTOM,(non-inverting mode)», при OCRxx=0x00 на выходе мк нет нулевого уровня, а присутствуют пики длительностью в такт таймера (смотрел осциллографом), что фактически равносильно OCRxx=0x01. При этом, естественно, чуть светится светодиод (а мощный — достаточно ярко) подключенный к этому выходу. Даташит это явление подтверждает (стр.104 для atmega16): «The extreme values for the OCR1x Register represents special cases when generating a PWM waveform output in the fast PWM mode. If the OCR1x is set equal to BOTTOM (0x0000) the output will be a narrow spike for each TOP+1 timer clock cycle. Setting the OCR1x equal to TOP will result in a constant high or low output (depending on the polarity of the output set by the COM1x1:0 bits.)». В результате приходилось использовать Phase Correct PWM. Вот мне и интересно, почему у Вас он регулируется от нуля?

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

  39. Доброе время суток!
    Написала программу (делаю ШИМ регулируемый резистором), компилирую, АВР-студия ругается на » .ORG $01C rjmp ADC ; ADC Conversion Complete Handler» , хотя как и другие вектора прерываний эта строчка скопирована из даташита! Но именно на ADC ругается. Как так?

    Еще такой нюанс — прерывание АЦПшника занимает больше места,чем другие? если два байта отведено, то он пишет про конфликт адресов : » C:\avr\pwm1.asm(38): error: Overlap in .cseg: addr=0x1e conflicts with 0x1c:0x1f »

    а если пишу вот так, то уже не ругается на адреса:

    .ORG $01C rjmp ADC ; ADC Conversion Complete Handler
    reti
    .ORG $020 rjmp EE_RDY ; EEPROM Ready Handler
    reti

    И еще выдает такую ошибку «C:\avr\pwm1.asm(93): error: Operand(s) out of range in ‘ldi r16,0x45f’ «, скажите, пожалуйста, что с этим делать!
    жду вашей помощи! )

  40. Ох, прошу прощения за дурацкий вопрос!! уже разобралась, там же перед rjmp надо было закомментить.. больше ничего вышеописанного Студия не выдает!!
    я просто читала где-то другой способ описания таблицы векторов, без .ORG, но почему-то он не работает) теперь почти все в порядке))

    1. И все же никак не могу устранить причину ошибки

      C:\avr\pwm1.asm(73): error: Operand(s) out of range in ‘ldi r16,0x45f’

      В чем может быть дело??

      з.ы. — а тут нельзя удалять свои комменты, если уже нашел ответ на вопрос? конечно, 7 раз подумай, 1 раз спроси, но так получилось..

      1. Пытаешься впихуть невпихуемое. Регистр может вместить значение не больше чем 255. А 0x45F нааамного больше. Такое число может влезть только в два байта (регистра) и обрабатываться побайтно. Как старший и младший байты.

        LDI R16,LOW(0x45F)
        LDI R17,HIGH(0x45F)

        1. Ошибка , похоже, была связана с корявой инициализацией стека, старший байт сначала не указала.

          Теперь все компилируется, шагает, но почему-то не выполняется простая команда :

          ldi R16, 0b11111111 ; В OCR 0*FF , на выходе будет сигнал выс.уровня
          (пока не придет значение с АЦП и не закинется в OCR)

          out OCR2, R16

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

          в табл.векторов написано:

          .ORG $000 rjmp RESET ; Reset Handler
          .ORG $002 ;rjmp EXT_INT0 ; IRQ0 Handler
          reti
          .ORG $004 ;rjmp EXT_INT1 ; IRQ1 Handler
          reti
          .ORG $006 ; rjmp TIM2_COMP ; Timer2 Compare Handler
          reti
          .ORG $008 rjmp TIM2_OVF ; Timer2 Overflow Handler
          .ORG $00A ; rjmp TIM1_CAPT ; Timer1 Capture Handler
          reti
          (и далее)

          А в обработчике прер-я:

          TIM2_OVF: in R17, ADCH ;
          out OCR2, R17

          reti ;выход из обработчика прер-я АЦП

          перед обработчиком — бесконечный цикл для ожидания прерывания..

          1. Чето у тебя какая то странная таблица векторов.
            Не поленись возьми и по даташиту выстави все вектора какие есть. На те которые надо поставьи RJMP, а ненужные затерминаль на reti.

            А прерывания разрешены локально и глобально? Без этого флажок может и встанет, но реакции не будет.

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

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

            1. Таблицу векторов я просто тут не стала всю размещать,вместо остальных векторов из даташита написано «(и далее)». Смотрела выше в описаниях ваших, вроде бы сделала все по аналогии. Т.е мне нужны только reset и по переполнению TIM2_OVF:

              .ORG $000
              rjmp RESET ; Reset Handler

              .ORG $002 ;rjmp EXT_INT0 ; IRQ0 Handler
              reti
              .ORG $004 ;rjmp EXT_INT1 ; IRQ1 Handler
              reti
              .ORG $006 ; rjmp TIM2_COMP ; Timer2 Compare Handler
              reti
              .ORG $008
              rjmp TIM2_OVF ; Timer2 Overflow Handler

              .ORG $00A ; rjmp TIM1_CAPT ; Timer1 Capture Handler
              reti
              .ORG $00C ;rjmp TIM1_COMPA ; Timer1 CompareA Handler
              reti
              .ORG $00E ;rjmp TIM1_COMPB ; Timer1 CompareB Handler
              reti
              .ORG $010 ;rjmp TIM1_OVF ; Timer1 Overflow Handler
              reti
              .ORG $012 ;rjmp TIM0_OVF ; Timer0 Overflow Handler
              reti
              .ORG $014 ;rjmp SPI_STC ; SPI Transfer Complete Handler
              reti
              .ORG $016 ;rjmp USART_RXC ; USART RX Complete Handler
              reti
              .ORG $018 ;rjmp USART_UDRE ; UDR Empty Handler
              reti
              .ORG $01A ;rjmp USART_TXC ; USART TX Complete Handler
              reti
              .ORG $01C ;rjmp ADC ; ADC Conversion Complete Handler
              reti
              .ORG $020 ;rjmp EE_RDY ; EEPROM Ready Handler
              reti
              .ORG $022 ;rjmp ANA_COMP ; Analog Comparator Handler
              reti
              .ORG $024 ;rjmp TWSI ; Two-wire Serial Interface Handler
              reti
              .ORG $026 ;rjmp SPM_RDY ; Store Program Memory Ready Handler
              reti

              1. А прерывания разрешены в TIMSK (это и называется локально?) и глобально — sei.

                Reset: ldi R16, (1<<DDB3) ; вывод ОС2 ( порт В, 3 бит) на выход
                out PORTB, R16

                ldi Temp, 0b01101100 ; настройка режима работы Т/С2, в т.ч. CLK/64, быстрый ШИМ и При совпадении ноль
                out TCCR2, Temp

                ldi Temp, 0b01000000 ; Bit 6 – TOIE2 установлен, разрешает прерывание по переполнению
                out TIMSK, Temp

                ; Инициализация стека (как в вашем примере)
                ;настройка АЦП

                ldi R16, (1<<ADLAR) ; выравнивание слева (результат в старших разрядах)
                out ADMUX, R16

                sei ;разрешить прерывания

                ;****************************************************
                ; ОСНОВНОЙ ЦИКЛ
                ;**************************************************
                begin:

                ldi R16, 0b11100000
                out ADCSRA, R16

                ldi R16, 0b11111111 ; ПОЛОЖИТЬ В OCR 0*FF ;
                out OCR2, R16 ; А НИЧЕГО НЕ ПРОИСХОДИТ …

                Inf: rjmp Inf ;бесконечный цикл — ждем прерывания

                TIM2_OVF: in R17, ADCH ; СЮДА ОН НИКОГДА НЕ ПОПАДАЕТ
                out OCR2, R17

                reti ;выход из обработчика прер-я АЦП

                Больше R17 нигде не используется, все равно сохранять в стек?

                1. УЖЕ СНОВА НАШЛА ОШИБКУ и заданные выше вопросы кажутся глупыми! =)

                  Проблема с прерыванием заключалась в том,что в таблице векторов у меня неправильно прописаны адреса, 000 -> 002 -> 004, я читала Ваш пример ШИМа и на автомате написала адреса как в примере, а не как в даташите на мою 8 атмегу :)

                  .ORG $000
                  rjmp RESET ; Reset Handler

                  .ORG $001 ;rjmp EXT_INT0 ; IRQ0 Handler
                  reti
                  .ORG $002 ;rjmp EXT_INT1 ; IRQ1 Handler
                  reti
                  .ORG $003 ;rjmp TIM2_COMP ; Timer2 Compare Handler
                  reti
                  .ORG $004
                  rjmp TIM2_OVF ; Timer2 Overflow Handler
                  (и прочие вектора)

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

                  А вот по команде (напишу ее пока так,как писала я) ничего не происходит:

                  ldi R16, 0b11111111 ; ПОЛОЖИТЬ В OCR 0*FF ; на выходе будет лог. 1
                  out OCR2, R16 ;

                  Но в общем-то программа может работать и без этого) Возможно, OCR нельзя обновить в то время, когда считает таймер-счетчик, и можно обновить только в момент переполнения , к началу нового периода.. ?

                  Теперь остался какой-то странный глюк, что значение в ОСR из АЦП кладется с отставанием .
                  Т.е кладу в АЦП 0200 (нужен старший байт,т.е в ОСR будет 02) . таймер переполнился, в обработчике прерывания кладем значение АЦП в OCR, но ничего не происходит, там ноль как и был.
                  Меняю значение в АЦП (пусть 03) в обработчике прерывания по переполнению таймера в OCR кладется… прежнее значение — 02.
                  По следующему переполнению в OCR положится 03 и т.д., т.е кладется не текущее значение, а предыдущее!

                  Буду искать причины) Спасибо за советы!

                  1. OCR можно обновлять в любое время. Шим правда немножко сбиваться будет, но не во всех режимах. В некоторых у него аппаратно выровняется на следующем проходе. В общем надо ДШ смотреть конкретней. Единственно, OCR 8ми разрядный или 16ти? Если 16ти, то там хитрый порядок доступа к его байтам. Сначала вроде бы младший, потом старший. Иначе его переклинивает. В даташите это отдельным пунктом оговорено в разделе про таймер (что то вроде подраздела «Доступ к 16ти разрядным регистрам). Почитай там.

                    А отставание насколько? Если на один период ШИМ, то вроде как так и должно (то самое выравнивание)

                2. Да, локально это бит разрешения конкретного прерывания. Ну а глобально флаг I в регистре sreg.

                  Если R17 нигде не используется и не будет использоваться, то можно не сохранять в стек. Главное про это ПОМНИТЬ! А то решишь потом заюзать и получишь вилы в бок.

    2. Без ORG тоже можно. Такое работает если длина вектора прерывания 2 байта, а мы туда ставим одну команду. В итоге, просто нужное количество RETI или RJMP. Но если взять контроллер у которого много памяти, то вектор там уже четырехбайтный, чтобы вместить большую команду JMP

      Ну либо делать этажерку RETI NOP, но это коряво.

  41. привет всем!!подскажите пожалуйста, нужно реализовать ШИМ но чтобы выходное значение быстро реагировало на опорное напряжение, т.е. задумка такая есть через ацп измерять, что то делать а потом кидать в регистр OCR…но загвоздка тут в том, что нужно оч оч часто обновлять значения регистра OCR а по программе этого не получается…есть два варианта использовать другой МК, в котором будет чисто измерение опорного напряжения и выдача ШИМ, либо второй — как то по мудрить с программой…может кто то сталкивался с проблемой, подскажите плиз.

    1. ШИМ у тебя один фиг должен перезарядить конденсатор, чтобы застабилизировать напряжение. Т.е. это два, а лучше три периода ШИМ импульса. Т.е. если ты хочешь реально быстро это сделать, то юзай внешний ЦАП. Например на R2R резистивной цепи (если не нужна большая разрядность) или спец микруху применить. На крайняк можно заюзать STM32 где ЦАП во многих чипах идет по дефолту.

  42. по про бывал сделать шим из таймера T1, сигналы вроде получил, но как то коряво получилось..проблема в том, что кидаю значения в регистры OCR1AH и OCR1AL…когда кидаю в младший регистр число(старший нули) на выходе не оч хорошая картина получается. если же на оборот в старший число(младший нули) на выходе прямоугольники на RC цепочке пила, но она не очень острая, заряжается не по прямой а по экспоненте…как сделать чтобы была прямая линия зарядки?? номинал изменить и еще объясните с регистрами, запутался.

        1. Самый лучший способ — применить AVR серии pwm или как то так. Там как раз будет 6 каналов ШИМ с dead time и прочими прелестями. Ну либо обычная мега с тремя каналами шим, на полумостовые драйверы.

            1. Красиво не получится. Единственный вариант — сделать тут по прерываниям сравнения и переполнения и включать-выключать выводы вручную. Но это будет во первых медленно, во вторых будет джиттер изза того, что прерывание может быть заблокировано другим прерыванием.

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

  43. У меня возник вопрос. Будет ли меняться уровень постоянной составляющей, при неизменной скважности, но разной частоте?
    Т.е. отношение сигнал/нет сигнала на выходе одно, а частота меняется, то подключенный, скажем, диод будет светить одинаково или нет?

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

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

    2. От фильтра зависит. По идее не должен. Но по факту того же диода может меняться яркость, т.к. глаз штука накопительная и если световой заряд в сетчатке глаза рассасывается быстрей чем набивается новый, то общая яркость будет падать.

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

  44. Здравствуйте

    А можно на аппаратном 8bit ШИМе ATmega16 (Timer0 или Timer2) обеспечить частоту 50Гц для управления серво?

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

    1. Даташит курил, после расчетов по формуле f_oc=f_clk/N*510 в Phase Correct PWM Mode пришел к выводу, что это надо подбирать кварц по-точнее и то, из доступных в продаже наименьшая погрешность оказалась 6% при частоте кварца 6,144000 МГц и делителе 256. А мне потом еще и по UART рулить этим контроллером предстоит, боюсь, что такой кварц с уартом уже совсем большую погрешность даст.

      неужели только программная реализация ШИМ для трех серво одновременно возможна (для ATmega16)?

  45. А как быть если OC3C на Atmega128 не работает и в протеусе и в железе?. точнее работает но до записи в OCR3C. стоит записать в регистр сравнение хоть что нибудь перестает работать.. в любом режиме шима.
    AvrStudio4, C++.

  46. и вообще возможно ли всеми 6 ШИМами пользоваться независимо друг от друга? то есть во все регистры сравнения забить разные значения чтоб все 6 каналов давали разные частоты? когда в регистрах одинаковые данные (OCR1A,OCR1B,OCR1C) всё работает а когда настроены по разному работает только OCR1A

    1. Есть вопрос.
      Речь о программном ШИМе.Автор пишет «Таким образом, на один таймер можно повесить дофига ШИМ каналов….».
      А можно ли сделать для каждого канала свою скважность тем способом которые указан в статье (на двух обработчиках прерываний)?если можно, то как?

      1. Можно, если частота небольшая. Можно сделать хоть десять каналов. На конечном автомате и одном таймере. Почитай статью «Управление множеством сервомашинок» там есть такое.

  47. Подскажите, почему нельзя в ходе цикла изменить OCR1AL???
    Как только пишу INC цикле, компилятор не ругается а Протеус мгновенно выстреливает с ошибкой.
    Вот пример кода:
    http://easyelectronics.ru/repository.php?act=view&id=89
    Предпологаю что надо обнулить TCNT …
    Но почему нельзя прямо???

  48. А можно ли с помощью таймера Т1 меги16 получить на одном из выходов таймера шим с частотой 100-300гц(для светодиода) а на втором выходе сигнал 2000гц для динамика. Но с условием — без использования прерываний, только на возможностях таймера??

      1. ОК.
        А можно ли получить частоту шима 2-2,5кгц, при такте контроллера 8мгц? Я ставлю пред делитель — 256, получаю такт таймера 31250гц и частоту ШИМа — 122гц. Биперу мало. Если пред делитель поставить — 8 , то шим получается 3.9кгц — биперу много. Можно ли как то выкрутиться, и получить шим с частотой 2-2,5 кгц.
        И попутный вопрос. Светодиоду пофиг что он будет ШИМится с такой большой частотой??

  49. Всем добрый вечер!

    Начал осваивать CTC(Clear Timer on Compare Match) режим таймера для генерации несущей, чтобы в дальнейшем воспользоваться ей для создания IR-канала.
    Как работает таймер в данном режиме вроде бы разобрался, НО без граблей не обошлось и в этот раз
    Собственно проблема заключается в том, что при установке вывода OC1A в качестве выхода (DDR_OC1A = 1) состояние вывода меняется не в выходном регистре (в моем случае это должен быть PORTB), а в регистре входа (PINB) данного порта. В датащите написано:

    The OC1A value will not be visible on the port pin unless
    the data direction for the pin is set to output (DDR_OC1A = 1).

    Но если ставить DDR_OC1A = 0, состояние вывода не меняется ни в входном, ни в выходном регистрах!
    Помогите пожалуйста разобраться в чем проблема? Допустил ли я ошибку в коде, или вывод в данном режиме может функционировать только как вход?

    1. Насколько я помню. ШИМ режимы не меняют PORT регистр, а сразу же воздействуют на сам вывод, что и отображается в PIN который является логическим отображением уровня на ноге. Если же тебе надо поймать сам факт сравнения и использовать, то лучше бери флаги событий (переполнение, сравнение) из регистра TIFR.

          1. Прошу прощения за быть может глупые вопросы, но если переводить эту фразу получается, что состояние OC1A не будет отображаться в регистре(PINB или PORTB уже не важно), до тех пор, пока DDR_OC1A = 1, т.е. установлен на выход. Следовательно, для того чтобы наблюдать значение OC1A в регистре(опять же не важно в каком), нужно установить DDR_OC1A = 0. У меня же все получается наоборот. При DDR_OC1A = 1 я вижу изменения, пускай и не в том регистре, что ожидал, а при DDR_OC1A = 0 — нет. Как это объяснить?

  50. Всем добра! Помогите новичку. Завел на tiny2313 аппаратный шим под управление сервой, причем угол поворота сервы должен меняться по входящим сигналам с UART. На испытаниях в железе сигналы приходят, однако серва нещадно тупит, реагирует через раз и неадекватно… причем если я пропишу движение сразу в код, без учета приходящих сигналов, все идет как надо. Где я напортачил? Заранее спасибо.

    http://easyelectronics.ru/repository.php?act=view&id=108 — главный цикл
    http://easyelectronics.ru/repository.php?act=view&id=109 — запуск UART
    http://easyelectronics.ru/repository.php?act=view&id=110 — запуск шим

  51. что означает «Еще есть возможность повысить разрешение, сделав счет 8, 9, 10 разрядным (если разрядность таймера позволяет)» ?
    тоесть у нас 16и разрядный таймер, частота кварца 12мгц.
    как я понимаю, в «обычном» Fast PWM частота шим максимум может быть 12000000/65535=183гц. совсем мало.
    а в режиме «Fast PWM 10 bit» у нас таймер превращается в 10 ибитный, так чтоли? тоесть его частота уже будет 12000000/1024=11,7 кгц, а скважность попрежнему будет меняться битами OCR1A?
    тогда что будет, если в OCR1A записать число больше 1024?

  52. Здравствуйте,
    подскажите пожалуйста, задача состоит в управлении мощностью двигателя с помощью
    ШИМ.
    Использую ATMega2560
    1.Значения для изменения регистров сравнения поступают через USART
    2.Используется прерывание (USART) приход нового символа
    3.Далее, запрещаю прерывания глобально и вызываю процедуру для изменения данных в регистре
    сравнения.
    4.Данные в регистрах сравнения изменяются как надо, но как только обратно разрешаю прерывание глобально, регистры сравнения возвращаются в первоначальное состояние.
    Если не трудно, подскажите пожалуйста, что делаю не так.
    Спасибо.

    1. Timsk это к прерываниям относится. ШИМ и без него работает.

      COМхх отвечают за поведение ноги при ШИМ. Зависит от выбранного режима.

      Для Fast PWM:
      00 — выключена и не реагирует на телепания всей этой логики.
      01 переключается в другое состояние при совпадении TCNT с OCR
      10 становится 0 при совпадении TCNT с OCR и становится 1 при естественном обнулении TCNT
      11 становится 1 при совпадении TCNT с OCR и становится 0 при естественном обнулени TCNT

      т.е. 10 и 11 одинаковые, но инверсные друг другу.

      Подробней смотри в даташите в разделе таймеров.

      На светодиоде ШИМ смотреть неудобно, плохо видно у светика очень нелинейная характеристика он вначале не горит нифига, а потом вспыхивает, подключи лучше стрелочный вольтметр.

      1. Вот именно я понять не могу это:
        10 становится 0 при совпадении TCNT с OCR и становится 1 при естественном обнулении TCNT
        11 становится 1 при совпадении TCNT с OCR и становится 0 при естественном обнулени TCNT
        OCR это что? В OCR мы записываем значение и сравниваем с ним ШИМ? OCR получается меняет скважность?

          1. Разобрался, ошибка была вообще не в регистрах:) Получилось заставить гореть светодиоды не в полную силу. Тперь не могу понять как менять скважность. Какой смысл этих строк?
            CLI ; Таймер меняется и в прерывании. Нужен
            ; атомарный доступ. Запрещаем прерывания
            OUT TCNT0,R16 ; Ноль в счетный регистр таймера
            STS TCNT,R16 ; Ноль в первый байт счетчика в RAM
            STS TCNT+1,R16 ; Ноль в второй байт счетчика в RAM
            STS TCNT+2,R16 ; Ноль в третий байт счетчика в RAM
            STS TCNT+3,R16 ; Ноль в первый байт счетчика в RAM
            SEI ; Разрешаем прерывания.
            ; Не совпало — не делаем :)
            NoMatch: NOP

            INCM CCNT ; Шарманка вращается дальше, вхолостую
            Где STS что будет если их убрать? И что такое INCM CCNT?

            1. Картинки не смотри @ комментарии не читай!

              Скважность задается в OCR регистре. Я же это в прошлом же комменте наглядно картинкой показал.

              Это всего лишь одна из возможных вариантов организации выдержки без использования таймеров, чисто на подсчете главных циклов. Т.н. счетчик-шарманка. Когда при вращении главного цикла Тикает программный счетчик TCNT (это я его так назвал, не путай с таймером TCNT0), а в самой программе события сравниваются с этим счетчиком. Если совпало — делаем. Ни к ШИМ ни к таймеру этот код в общем случае отношения не имеет.

          2. Извиняюсь что не по теме, но. Студия меня сегодня огорчила! Сначала она висанула и не открыла мне проект. Потом проект открылся, но со 2 раза. Он не компилировался на F7 я закрыл его и открыл заново! Пустой лист!!! Клавиша F7 в студии не работает и другие горячие клавиши скорей всего тоже. Что делать? Как восстановить по умолчанию клавиши?

  53. LD R16,X
    OUT OCR0, R16
    LD R16,Y
    OUT OCR1AL, R16
    LDI R16,0
    OUT OCR1AH,R16
    LD R16,Z
    OUT OCR1BL, R16
    LDI R16,0
    OUT OCR1BH,R16

    регистры сравнения не устанавливаются. По адресам в указателях XYZ значения отличны от нуля, АВРСтудия ошибок не выдает. Что не так?

        1. Какой контроллер используется? Возможно, что у него эти регистры обьявлены как Memory Mapped и тогда команды IN OUT для них не применимы и нужно будет применять LDS STS команды.

          1. ;конфигурация таймеров 0, 1А, 1В
            OUTI TCCR1A,2<<COM1A0|2<<COM1B0|0<<WGM11|1<<WGM10
            OUTI TCCR1B,0<<WGM13|1<<WGM12|5<<CS10
            OUTI TCCR0,2<<COM00|1<<WGM01|1<<WGM00|5<<CS00

            Если "закоментить" первую и третью команды OUTI, регистры OCRx устанавливаются

  54. Уважаемый DI HALT, помогите разобраться — сижу грызу даташит , наткнулся на такую комбинацию битов WGM — 1111. В Вашем примере WGM — 0101 вроде все понятно 8 бит — значение TCNT $FF , т.е. счетчик тикает от 0 до 255. это я так понимаю и есть модуль счета. А в примере том сказано , что модуль счета определяется OCR. Как это понять? Чем отличаются эти команды?

  55. Добрый день, DI HALT.
    Скажите как называется ваш осциллограф и сколько он приблизительно стоит. Я хочу себе купить такой-же, потому-что я до сих пор пользуюсь старым советским ламповым осциллографом, а на нем ШИМ хрен разберешь!

    1. У меня Rigol 1042CD покупал я его за 40тыр. Сейчас он снят с производства и есть другие. Цена около 300…1000 баксов, в зависимости от модели. Рекомендую Rigol1042CD или им подобные.
      http://toolboom.com/ru/Measuring-Equipment/Oscilloscopes-RIGOL вот тут посмотри. Бери какой понравится, частоты от 40мгц хватает практически на все.

  56. Доброго времени суток. ВОзник вопросс: Использую таймер в Atmega16 для генерации АППАРАТНОЙ ШИМ. Могу ли я каким-нибудь образом параллельно с этим использовать таймер и в «классическом» режиме для отсчитывания временного интервала и ухода в прерывания, когда надо? Ясное дело, что можно ставить прерывание по переполнению, но в таком случае этот интервал будет всегда константа, а меняя его я сломаю ШИМ…Можно как ниб реализовать эти два процесса параллельно на одном таймере?

    1. На точных отсчетах сомневаюсь, но можно поизвращаться. Что у нас есть — есть таймер тикающий от 0 до MAX и генерящий ШИМ.

      Если свободен второй шим регистр (если он есть у этого таймера) то можно меняя его OCR иметь задержки от 0 до OCR и ловить их по сравнению с этим значением, работая на прерывании этого регистра. Работе шим на другом OCR регистре это не помешает никак.

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

  57. Добрый день, делаю на tiny13 ШИМ регулятор
    управлять буду светодеодной лентой т.е. ток вобщемто небольшой.
    так вот план1 такой:
    1. на выходе контроллера повешать RC цепочку. таким образом получится регулировка от 0 до 5В
    2. Посел этого поставить полевик например IRL540 ( как раз полностью откроется на 5В). Таким образом будет плавная регулировка яркости ленты

    план2:
    1. Выход микроконтроллера подключить на гейт полевика и таким образом полуить мощный шим сигнал на выходе с полевика
    2. подклчить к нему ленту и параллелно кондер к земле

    Вопросы по первому плану:
    будет ли такая схема работать и достаточно ли она проста и оптимальна для моей задачи?
    не будел ли полевик греться когда будет работаь в «полуоткрытом сосстоянии»? точнее даже так, греться он конечно будет, но ток малеький, поэтому думается что нагрев он выдержит?

    Впоросы по второму плану:
    правильно ли я понимаю что в данном случае нужно сделать так что бы кондер заряжался через сопротивление ленты и тогда поулчится таже самя RC цепочка но олько уже после полевика
    Можно ли избавится он кондера в цепи и податть на ленту шим сигнал?

    Общий вопрос:
    Какой план лучше всего исопльзовать для задачи ругулировки яркоти светодиодной ленты?

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

      Полевик конечно же будет греться и будет греться сильно (квадрат тока на его полуоткрытое сопротивление).

      Так что гони на ленту шим сигнал с частотой под 30кгц и будет тебе счастье.

  58. Здравствуйте.
    Разбираюсь в первой части ШИМ. Студия 4.18 при выполнении этого кода
    SETB DDRD, 4, r16 ; PortD.4 выход
    SETB DDRD, 5, r16 ; PortD.5 выход
    OUTI TCCR1A, 2<<COM1A0 | 2<<COM1B0 | 0<<WGM11 |1<<WGM10
    OUTI TCCR1B, 0<<WGM13 | 1<<WGM12 | 1<<CS10
    cli
    OUTI OCR1AH,0
    OUTI OCR1AL, 85
    OUTI OCR1BH,0
    OUTI OCR1BL, 128
    sei
    показывает, что "1" на выходе ОС1В встает существенно раньше , чем на ОС1А. Почему? На картинке в статье "1" встают одновременно…

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

      1. Разобрался. Ответ в постах за 1-е Февраля 2009г. Di, а какую сейчас люди Студию используют, чтоб с минимумом глюков? А то твой Pinoboard 2 идет в Киев уже 16 дней, наверное я стал жертвой торговой войны …

        1. По моему скромному мнению, наиболее безглючной версией Студии является 4.18 (сервис-паки я не ставил ). Использую ее дома на Win7X64, на работе в W2k на древнем ноуте, пробовал и в XP32. Во всех случаях Студия работает стабильно, отладка через JTAG v.1 (и через железный COM-порт, и через USB-UART конвертеры)работает прекрасно, отлично интегрируется с WinAvr последней версии(при желании — и с AVR-GCC тулчейном). Найти можно на сайте Атмела.

  59. Всем доброго времени суток!Подскажите пожалуйста.Я в деле изучения микроконтроллеров начинающий.Пытаюсь написать программу ШИМ генератора для ATtiny2313.В регистры управления таймером 0 записано следующее: ldi temp,0b01000011
    out TCCR0A,temp
    ldi temp,0b00001001
    out TCCR0B,temp
    Получается режим Fast PWM, при совпадении с содержимым регистра сравнения происходит переключение вывода ОС0А,коэф предделителя равен 1.Запускаю проект в AVRStudio и наблюдаю следующее: при достижении значения счетного регистра TCNT0 значения содержащегося в регистре сравнения происходит обнуление счетного регистра, хотя этого быть не должно,должен же только переключиться вывод ОС0А а счетчик должен досчитать до 255 и обнулиться!в ЧЕМ ЗДЕСЬ ДЕЛО??Помогите пожалуйста!

  60. Здравствуйте.
    Не очень понятно с предделителем таймера, во многих ответах на вопрос как увеличить частоту ШИМ рекомендуют ставить более быстрый кварц. А как же уменьшение предделителя до 8ми или вообще до единицы(получается при работе от внутреннего мегагерцового RC генератора ШИМ будет тоже 1МГц)….или так нельзя? Объясните этот момент пожалуйста.

    1. Это делитель тактовой частоты которая идет на вход таймера. А период ШИМ сигнала это пробег таймера от 0 до MAX. Т.е. его полюбому делить придется на разрядность.

    1. Само собой, частота ШИМ Зависит от предделителя таймера, но на 1МГЦ, если надо получить высокочастотный ШИМ сигнал 1МГЦ может и не хватить, особенно если разрядность у нас 10 бит.

  61. В инете насчет ШИМа где только не рылся, и везде предделитель ставят 256, будто бы надо обязательно делить на разрядность.
    Выходит если кварц поставить на 20 МГц то и ШИМ реально получить на 20 МГц …….нехило ведь получается?

    1. Не знаю где какой предделитель ставят, но к разрядности ШИМ он отношения не имеет от слова совсем. Только к скорости. Далеко не всегда нужна частота предельная.

      С кварцем 20мгц ты получишь шим 20/256 мгц. Это если 8 разрядов. Или 20/1024 если 10.

  62. Чем больше я знаю, тем меньше я понимаю (
    Это понятно, разрядность и предделитель вещи разные…
    Но почему «С кварцем 20мгц ты получишь шим 20/256 мгц. Это если 8 разрядов. Или 20/1024 если 10.»
    ты именно что делишь на разрядность — 8разрядов = 256, 10 разрядов = 1024?

  63. Вроде начал понимать.
    Ну тогда фраза «Выходит если кварц поставить на 20 МГц то и ШИМ реально получить на 20 МГц …….нехило ведь получается?» выходит что бред?

  64. Из статьи:
    Частота получившегося ШИМ сигнала определяется просто: Частота процесора 8Мгц, таймер тикает до 256 с тактовой частотой. Значит один период ШИМ будет равен 8000 000/256 = 31250Гц. Вполне недурно. Быстрей не получится — это максимальная скорость на внутреннем 8Мгц тактовом генераторе.
    Почему не делил на предделитель?

  65. Всем привет. Подскажите пожалуйста как получить третий шим? По сути три выхода на три светодиода, которые будут гореть с разной яркостью. Пишу в avrstudio5 на ассемблере. В статье прочитал что можно до 4 шим реализовать на atmega16 , но нужно только три.

      1. Не пойму все равно?
        Для установки шим выставил так:
        ldi temp,0xF9
        out TCCR1A,temp
        ldi temp,1<<CS10
        out TCCR1B,temp

        По сути формирую синусоиду:
        Set_PWM:
        Ldi ZH, High(sine_table*2) ;выход шим на ножке PB5 (OC1A)
        Ldi ZL, Low(sine_table*2)
        Clr Temp
        Add ZL, TablePtr
        Adc ZH, Temp
        Lpm
        out OCR1AH,temp
        out OCR1AL,R0
        Add TablePtr,Skipper

        Ldi ZH, High(sine_table1*2) ;выход шим на ножке PB6 (OC1B)
        Ldi ZL, Low(sine_table1*2)
        Clr Temp
        Add ZL, TablePtr1
        Adc ZH, Temp
        Lpm
        out OCR1BH,temp
        out OCR1BL,R0
        Add TablePtr1,Skipper

        Ret
        …….. далее две таблица синуса все работает . Две синусоиды получил.
        Нужна третья.)

  66. Не буду весь код писать.
    Для вывода шим на ножку PD5 (OC1A) будет к примеру так:
    clr temp
    out OCR1AH,temp
    ldi temp,125
    out OCR1AL,temp

    На ножку PD4 (OC1B):
    clr temp
    out OCR1BH,temp
    ldi temp,125
    out OCR1BL,temp

    ….. Как теперь подать шим на ножку PD7/OC2 . Ставил OCR1СH . AVRstudio5 сразу ошибку выдает не зависимо от контроллера. Пробовал устанавливать это же значение OCR1СH , OCR1СL на atmega128 на выход OC1C . То же самая ошибка: Operand 1 out of range: 0x79 .

    1. А они возможно Memory Mapped т.е. загрузка в них через out не работает. Открой m128Adef.inc и погляди какие из периферийных регистров отмечены как размещенные в памяти.

      И к тем регистрам другой подход. Загрузка командой sts, а выгрузка lds

  67. Как получить при помощи шим синусоиду переменного тока на транзисторных ключах. Пытаюсь разобраться как управлять асинхронным трехфазным двигателем. Есть векторное управление, где поочередно включаются транзисторы в нужном порядке. Понимаю что нужен еще опорный сигнал шим. По сути сначала нужно получить синусоиду через таблицы и т.д. После этого сгладить конденсатором и полученный сигнал подать на один вход компаратора, а на другой опорное напряжение так что бы поймать ноль, при котором сигнал на выходе компаратора будет инвертирован. Синхроимпульс как бы получил таким образом для полупериода. Теперь через логику к примеру (OR) нужно подать шим сигнал и собственно синхроимпульс для определенной пары транзисторных ключей и в таком же порядке по наступлению обратного синхроимпульса на компараторе так же через логику инвертировать шим сигнал для перехода к нижнему полупериоду. Правильный у меня ход мыслей или Я запутался и все совсем по другому? Уже и схему составил в протеусе и код для трех синусоид получил, но на выходе катушки получается гармошка. Башку уже сломал. Помогите советом.

      1. Да вот пытаюсь разобраться. То есть получается что векторное управление это просто либо есть напряжение на обмотке, либо нет и так поочередно и значит низкий КПД в отличии от чистого синуса на выходе. В принципе как синус получить на выходе тогда?

        1. Не совсем, если либо есть-либо нет то это мы получим неравномерность. Лучше все же плавно менять напряжение, делается это шимом.

          Синус делается просто ШИМом по таблице или еще как-то. Сглаживается он на индуктивностях обмоток сам уже.

          1. Кажется у меня получилось в протеусе собрать схему частотника для трехфазного движка, но выглядит как то уж очень просто. Контроллер использовал atmega64. Программно через таблицы синусов на ножках где OC1A, OC1B, OC1C получил три синусоиды со сдвигом 120 градусов как положено. Выстроил шесть ключей и соединил их через логику NOT так что если на каждой паре 1 , то открыт верхний транзистор , 0 нижний. В качестве подобия мотора соединил три катушки в треугольник и подсоединил к ключам. На входы ключей просто подал сигналы от OC1A, OC1B, OC1C и о чудо на выходе каждой катушки синусоида. Неужели все так просто или это во обще самый банальный принцип с низким КПД? Просто как не посмотришь и у кого не спросишь — частотник это какая то космическая штука.

            1. Открою тебе страшную тайну секты электроприводчиков. Частотный привод это не управляемая синусоида, это возможность задать желаемую характеристику привода. В частности прямоугольную. Т.е. постоянство заданного момента во всем диапазоне скоростей. Для этого надо теперь все это обвесить обратными связями по моменту и оборотам. И вот тут много матана, ТАУ и сложностей.

              А тройным синусом крутить можно, только смысла немного.

              1. Обратные связи. То есть Я подаю на ключи допустим 001. В этот момент открыт верхний транзистор и два соседних нижних. Если звездой, то ток течет через катушку скажем А и частично B и C . Движок пытается сделать какой то поворот. В этот момент что должно произойти, поступить сигнал от датчика с одной из фаз на основе которого будет разрешено дальнейшее переключение или подача в этот момент импульсов шим параллельно определенному ключу?

                1. Это принцип BLDC. А я немного не о том. Как вращать поле зависит от конкретной реализации и используемой машины. Можно по датчикам положения, можно в асинхронном режиме, можно в синхронном. Я про контроль за оборотами и моментом.

                  А вообще откройте сайт на главной и там будет парочка статей про электропривод. Они в первой десятке последних постов. Там будет ряд моментов про это.

                  1. На главной нашел статью про двигатель переменного тока. Это принцип его работы, да хорошо описано понятно. Но все же то чего пытаюсь понять, никак. …. Давай попробуем так. Есть батарея 24В. Шесть мощных IGBT . Что на входы этих IGBT должен подавать контроллер?

              2. Я хочу понять простую вещь. Не нужно пока супер пупер понимания с обратной связью, максимального КПД и тому подобного. Мне хочется просто принцип понять. Есть контроллер допустим atmega64 . Есть 6 ключей. Есть трехфазный движок, соединенный с этими ключами. Я хочу приложить в момент работы осциллограф к обмоткам двигателя и увидеть на экране 3 синусоиды. Что должен контроллер для этого делать? Формировать последовательность на входы ключей 001,011 и т. д. или все же по таблицам формировать три сигнала шим в которых заложены три синусоиды и подавать уже их на входы ключей и каким образом?

                1. Чтобы понять эту простую вещь надо понять какой двигатель ты хочешь запустить. Трехфазные движки разные бывают.

                  АД — можно раскрутить тупой синусоидой трехфазной. Гонишь ее шимом по таблице на свой мост и готово. Главное чтобы частота была достаточная. Обычно хватает 50ГЦ.

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

                  BLDC — запуск последовательностью дискретных напряжений на ключи в зависимости от поворота ротора. Электронный коллектор по сути.

                  1. Двигатель асинхронный без лишних заморочек 50Гц. Хорошо, значит синусойдой тупо шимом на входы моста могу раскрутить. Теперь интересно, если синусоиду программно буду от 0 до 50Гц менять, то в идеальном случае момент на валу будет постоянным?

                    1. То есть получается что если подать 10Гц на вход, то 60*10/3 = 200об/мин или 50Гц, тогда 60*50/3=1000об/мин …..то двигатель на 200об и на 1000 оборотах будет иметь разный момент? Имею ввиду именно в момент когда он достигнет этих двух значений 200 и 1000.

                    2. Тогда по ходу моих догадок что бы момент был и на 200 и на 1000 оборотах при частотах 10 и 50Гц как уже писал ранее необходимо на входы ключей моста подавать большее или меньшее напряжение импульсов шим, как бы подстраивать систему так что ли?

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

                    4. В принципе все начинает прояснятся в голове.) Кстати насчет часто озвученной по теме АД третей гармонике, там где синусоида проваливается на мокушке синуса). Это как Я понимаю так же можно формируеть некими корректировками в таблицах синуса?

                    5. Не скажу, т.к. не помню что там с третьей гармоникой :)

                    6. Иду шаг за шагом дальше). С основами управлением благодаря твоей помощи разобрался, спасибо. …. Теперь хочу понять как управлять напряжением при разных частотах. Собственно задача поддерживать постоянный момент на всех оборотах. Есть много нашумевшие по всей сети драйверы IR. В протеусе есть IR2101 не суть. По принципу его работы то же понятно. На нижнем ключе напряжение питания самого драйвера V+ и при переключении на верхний ключ питание от кондера. Теперь что хочу понять. Вот Я подаю импульсы ШИМ как уже описывал ранее только не на прямую а на драйвер и поскольку этот драйвер подает на входы ключей именно питание самого себе, то значит можно изменяя это напряжение V+ регулировать амплитуду синуса на моторе, то есть поддерживать ее постоянной разумеется там через отслеживание самой частоты и программно уже подавать на драйвер большее или меньшее напряжение питания, которое он уже будет тыкать на входы ключей. Есть также другие мысли что можно исходя из частоты амплитуды регулировать общее напряжение самого моста с ключами.

  68. Во-первых, огромное спасибо за цикл статей, очень помогло разобраться, понять и запустить.
    Во-вторых, вопрос (увы). Есть разведенная плата на ATMega8L, Стоит кварц 8МГц, что соотвествует длительности одного периода 0,125 мкс. Использую таймер 1 в режиме СТС на максимальной скорости (т.е. OCR1A = 0x0000) без всяких предделителей. Задай синхроимпульс дерганием ноги (PORTD.6=!PORTD.6). По идее период получившегося импулса должен быть 0,25 мкс. На практике упираюсь 2 мкс и все. Причем получается, что изменения половины младшего байта не имеет никакого значения. FUSE биты проверял. В чем может быть проблема?

  69. Привет Di! Вопрос. Зачем в Шим на прерываниях устанавливать биты wgm. Прерывание генерится как по переполнениъ, так и по сравнению при wgm 00. Можно ведь шим в 16 разрядов юзать для timer1. Или не так?

  70. Ребят, подскажите, в коде ошибка конфликт адресов

    .include "m16def.inc"

    .org $000
    rjmp main
    .org $026
    rjmp Timer0_OC ; (TIMER0 COMP) Timer/Counter0 Compare Match
    .org $012
    rjmp Timer0_OVF ; (TIMER0 OVF) Timer/Counter0 Overflow
    .org $00C
    rjmp Timer1_OC ; (TIMER1 COMPA) Timer/Counter1 Compare Match A
    .org $010
    rjmp Timer1_OVF ; (TIMER1 OVF) Timer/Counter1 Overflow
    .org $006
    rjmp Timer2_OC ; (TIMER2 COMP) Timer/Counter2 Compare Match
    .org $008
    rjmp Timer2_OVF ; (TIMER2 OVF) Timer/Counter2 Overflow

    main:
    ldi r16,0xFF
    out ddrb,r16

    ldi r16,low(RAMEND)
    out SPL,r16 ;
    ldi r16,high(RAMEND)
    out SPH,r16

    ldi r16,0b11110111; используем прерывания по совпадению и переполнению на всех счетчика
    out TIMSK,r16 ; кроме TCCR1B

    ldi r16,0
    out OCR1AH,r16
    ldi r16,170 ; константа для совпрадения (255/3)*2 = 170
    out OCR1AL,r16 ; константа на все счетчики
    out OCR0,r16
    out OCR2,r16

    ldi r16,0 ; нулевой счетчик в 0
    out TCNT0,r16
    out TCNT1H,r16
    ldi r16,85 ;первый счетчик 85
    out TCNT1L,r16
    ldi r16,170 ;второй счетчик 170
    out TCNT2,r16

    ldi r16,0b01001101 ; 0,1,2 - 1/1024 ,3,6 - Fast PWM , 4,5 - прямой ШИМ при совпадении
    out TCCR0,r16 ;
    ldi r16,0b01001111 ; единицы в 0,1,2 т.к.предделитель во втором счетчике более точный 1/1024
    out TCCR2,r16
    ldi r16,0b00001101 ; 0,1,2 - 1/1024 3,4-WGM12,WGM13 соответственно совместно с WGM10
    out TCCR1B,r16 ; и WGM11 выставляют режим Fast PWM;WGM13-0;WGM12-1;WGM11-0;WGM10-1 Это 8-бит fastpwm
    ldi r16,0b00000010 ;0,1 - WGM10,WGM11 cоответственно 4,5,6,7 - прямой ШИМ
    out TCCR1A,r16
    sei
    loop:
    rjmp loop

    Timer0_OC:
    sbi PORTB,1
    reti
    Timer0_OVF:
    cbi PORTB,1
    reti
    Timer1_OC:
    sbi PORTB,2
    reti
    Timer1_OVF:
    cbi PORTB,2
    reti
    Timer2_OC:
    sbi PORTB,3
    reti
    Timer2_OVF:
    cbi PORTB,3
    reti

  71. Возник еще такой вопрос, настроил первый таймер на fast pwm 8-бит, а в симуляторе показывает, что у меня ШИМ работает как Phase correct, да и движок, когда вращается почему-то дергается и останавливается. Подскажите пожалуйста
    ldi r16,0b00001001
    out TCCR1B,r16
    ldi r16,0b00000001
    out TCCR1A,r16

  72. Извините, что так часто задаю вопросы — разрабатываю лабораторку по запуску движка. Не могли бы вы подсказать как в Phase Correct PWM задать начальную точку отсчета в обратной части счета? Делал такую штуку:
    ldi r16,255
    out tcnt0,r16

    ldi r16,170
    out tcnt0,r16
    В симуляторе задается в обратной части, как надо, а в действительности в прямой — две фазы совпадали

  73. «Прескалер будет равен 64, таким образом, частота тиков таймера составит 125000 Гц. А нам надо прерывание с частотой 1000Гц. Поэтому настраиваем прерывание по совпадению с числом 125…»
    Я туплю и ли всё же не со 125, а (125-1)?

  74. Здравствуйте, прошу подтолкнуть
    Как сделать два канала шим с возможностью контролированного сдвига относительно друг друга (какой алгоритм действий ? ). Пытаюсь реализовать на beaglebone black через mmap и open.

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

      1. Спасиби за ответ, да именно так поэтому минуя виртуальную файловую систему, через драйвер у меня есть доступ к ядру и регистрам таймеров и портов. Не могли бы вы натолкнуть как правильно организовать алгоритм работы двух шим с временной сдвижкой относительно друг друга?. Полагаю что нужно три таймера, где один после запуска первого через время толкает второй. Как бы это выглядело на stm?

        1. В STM32 я бы все сделал аппаратно и не парился. А тут придется городить конечный автомат. Посмотри у меня статью «управление множеством сервомашинок» задача похожая.

  75. Вопросы о таймерах и ШИМ (у меня ATMEGA 328P).
    1. Таймер 0 — 8 бит. Режим СТС. Два независимых канала OCR0A и OCR0B одновременно не работают. Тестировал так: в main пишу в OCR0A один код, а в OCR0B — другой. Ловлю прерывания по каждому каналу и вывожу сигналы на 2 светодиода. Если число в OCR0B > OCR0A, то прерывания по каналу В нет. Если OCR0B < OCR0A, то прерывание в В есть, но с частотой равной прерыванию А. Курение даташита показывает, что там этот вопрос скромно не раскрыт.
    2. Так и не понял как работает режим FAST PWM в режиме WGM02:0 = 7. Каким событием сигнал на выходе устанавливается в 1, а каким в 0. Даташит не дает ответа.

Добавить комментарий