Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: ШИМ
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Страницы: 1, 2
ps1x
Как сделать шим на 3 канала с одним таймером? Использовать принцип динамической индикации?
p.s. Надо подключить rgb светодиод.
rezident
Наверное стоит уточнить: программный ШИМ или аппаратный?
SasaVitebsk
Цитата(rezident @ Oct 7 2007, 20:54) *
Наверное стоит уточнить: программный ШИМ или аппаратный?

А также какой таймер. Например t1,t3,t4,t5 имеют в своём составе OCRxA,B,C.
ps1x
Ну например, можно ли реализовать 3 канала ШИМ на tiny26?
rezident
Цитата(ps1x @ Oct 8 2007, 03:46) *
Ну например, можно ли реализовать 3 канала ШИМ на tiny26?

ps1x, вы партизан, разведчик или попугай? laughing.gif Что вы заладили можно ли, можно ли? Вас пока еще русским языком просят озвучить требуемые вам параметры ШИМа (период, шаг/количество ступеней и т.п) и предполагаемый способ его реализации: программный или аппаратный?
ps1x
Цитата(rezident @ Oct 8 2007, 02:22) *
ps1x, вы партизан, разведчик или попугай? laughing.gif Что вы заладили можно ли, можно ли? Вас пока еще русским языком просят озвучить требуемые вам параметры ШИМа (период, шаг/количество ступеней и т.п) и предполагаемый способ его реализации: программный или аппаратный?

Ни то ни другое. Надо подключить РГБ светодиод, с плавным изменением цвета, и в целом, способ достижения этого мне не важен, важно понять как... А параметры ШИМ 256 уровней 5кГц.
add
Один из вариантов программно так: устанавливаем таймер на нужную частоту.(5кгц) По прерыванию инкрементируем значение переменной (byte) (256). Сравниваем это значение с переменной каждого канала(переменные которые,в нашем случае, задают яркость св.диода). Если значение переменной яркости канала больше/меньше значения инкрементируемой переменной, то выставляем порт в 0/1. Все.
Записывая нужные значения в переменные яркости канала получаем нужную нам яркость на св.диоде.
Зы: Предвижу замечания по поводу коррекции значений констант для значений яркости..из-за особенностей восприятия свечения глазом.. и т.п. вещи..
smile.gif но я обьяснил только принцип программной реализации шима.. Будет вопрос по линейности зажигания/гашения диодов обсудим..
beer.gif
Зы2: Для 5кГц 256 значений многовато.. Надо делать меньше значений либо прерываня хотыбы 25кГц.. тогда 100Гц мерцания видно уже не будет.
=GM=
Цитата(ps1x @ Oct 7 2007, 21:42) *
Надо подключить РГБ светодиод, с плавным изменением цвета, и в целом, способ достижения этого мне не важен, важно понять как... А параметры ШИМ 256 уровней 5кГц

Вот тут было обсуждение. Вообще, не стесняйтесь пользоваться поиском, многие вопросы на форуме "обсосаны до костей".
ps1x
Цитата(=GM= @ Oct 8 2007, 15:38) *
Вот тут было обсуждение. Вообще, не стесняйтесь пользоваться поиском, многие вопросы на форуме "обсосаны до костей".

к сожалению, поиск по словам ШИМ и PWM ничего не дал (сдается, ибо слово короткое)
=GM=
Цитата(ps1x @ Oct 8 2007, 10:54) *
к сожалению, поиск по словам ШИМ и PWM ничего не дал (сдается, ибо слово короткое)

Да ладно, всё ищется на раз. Я ж вам дал ссылку http://electronix.ru/forum/index.php?showtopic=23143, вы туда ходили?

Вот ещё ссылка на телесиси http://www.telesys.ru/wwwboards/mcontrol/1...ges/37530.shtml
Там правда обсуждалась 8-ми канальная шим, ниже приведен кусочек для 3-х каналов, разбирайтесь.
Код
void interrupt isr(void)
{
if(--refr)
{
  if(--(chan1)) portb |=0x01;   //нога pb0
  if(--(chan2)) portb |=0x02;   //нога pb1
  if(--(chan3)) portb |=0x04;   //нога pb2
}
else
{
  chan1 = value1;               //уставка 1
  chan2 = value2;               //уставка 2
  chan3 = value3;               //уставка 3
  refr = 255;                   //счетчик квантов шим
  portb = 0;                    //начало шим
}
}
ps1x
Цитата(=GM= @ Oct 8 2007, 16:18) *
Да ладно, всё ищется на раз. Я ж вам дал ссылку http://electronix.ru/forum/index.php?showtopic=23143, вы туда ходили?

Вот ещё ссылка на телесиси http://www.telesys.ru/wwwboards/mcontrol/1...ges/37530.shtml
Там правда обсуждалась 8-ми канальная шим, ниже приведен кусочек для 3-х каналов, разбирайтесь.
Код
void interrupt isr(void)
{
if(--refr)
{
  if(--(chan1)) portb |=0x01;   //нога pb0
  if(--(chan2)) portb |=0x02;   //нога pb1
  if(--(chan3)) portb |=0x04;   //нога pb2
}
else
{
  chan1 = value1;               //уставка 1
  chan2 = value2;               //уставка 2
  chan3 = value3;               //уставка 3
  refr = 255;                   //счетчик квантов шим
  portb = 0;                    //начало шим
}
}

Да спасибо smile.gif
SasaVitebsk
Используя таймер 1 и выводы OC1A,B,C с соответствующими регистрами, можно всё это реализовать проще и точнее. Программы практически не будет. В принципе останется инициализация ну и задание яркости по каналам с помощью присваивания. Если необходима высокая частота, то придётся дополнительно обработать одно прерывание t1ovf. В зависимости от частоты можно получить до 16 бит на канал - то есть 48 бит на цвет.
=GM=
Цитата(SasaVitebsk @ Oct 10 2007, 13:25) *
Используя таймер 1 и выводы OC1A,B,C с соответствующими регистрами, можно всё это реализовать проще и точнее. Программы практически не будет

Сиё называется аппаратный шим(:-).

Кстати, Александр, недавно вы упоминали реализацию на си 12 программных шимов с периодом 5 кГц. Сколько разрядов у шима удалось реализовать?

Спрашиваю потому, что тоже реализовал 24 канала шим с частотой 6 кГц, правда, на ассемблере. Шим был 8-разрядный, т.е. 256 позиций. Клок 20 МГц.
alexander55
Цитата(=GM= @ Oct 11 2007, 02:58) *
Спрашиваю потому, что тоже реализовал 24 канала шим с частотой 6 кГц, правда, на ассемблере. Шим был 8-разрядный, т.е. 256 позиций. Клок 20 МГц.

Это круто. Но возникает вопрос, а больше ничего делать не надо, только крутить ШИМы ?
SasaVitebsk
Цитата(=GM= @ Oct 11 2007, 01:58) *
Сиё называется аппаратный шим(:-).

Кстати, Александр, недавно вы упоминали реализацию на си 12 программных шимов с периодом 5 кГц. Сколько разрядов у шима удалось реализовать?

Спрашиваю потому, что тоже реализовал 24 канала шим с частотой 6 кГц, правда, на ассемблере. Шим был 8-разрядный, т.е. 256 позиций. Клок 20 МГц.


Там был программно аппаратный ШИМ завинченный под управление двигателями. Точность 3200 в принципе определяется частотой (16М). То есть таким образом при 256 полочках можно теоретически довести частоту до 62кГц. Но в реальности, естественно всё выглядит хуже. smile.gif Мне в двигателях не надо находится в произвольном месте ШИМа. Я нахожусь в фиксированных точках синуса. Поэтому практически могу обслуживать любое число двигателей. Это определяется только наличием свободных портов.

В 1989 году на однокристалке 1816ве48 (8048) (7МГц) реализовал ДУ для телевизора. smile.gif Там был плавный программный ШИМ на 4 канала с частотой 100Гц и 64 ступеньки. smile.gif

В принципе число каналов ШИМ практически не влияет на характеристики самого ШИМа. На него влияет только производительность процессора и его аппаратные возможности. При построении программного ШИМа неизбежно сталкиваешься с погрешностями на краях. То есть при слишком малых и при слишком больших значениях. Естественно при высокой частоте ШИМа.

2 GM как ты с этим боролся. Я никак. Просто констатировал ошибку. smile.gif
=GM=
Цитата(SasaVitebsk @ Oct 11 2007, 08:01) *
Там был программно-аппаратный ШИМ завинченный под управление двигателями. Точность 3200 в принципе определяется частотой (16М). То есть таким образом при 256 полочках можно теоретически довести частоту до 62 кГц

Кажется, мы друг друга не понимаем(:-). Где там? Что такое точность 3200? Частота до 62 кГц для 12 шим? Программно или аппаратно?
Цитата(SasaVitebsk @ Oct 11 2007, 08:01) *
Мне в двигателях не надо находится в произвольном месте ШИМа. Я нахожусь в фиксированных точках синуса. Поэтому практически могу обслуживать любое число двигателей. Это определяется только наличием свободных портов

Почему только фиксированные точки синуса?
Цитата(SasaVitebsk @ Oct 11 2007, 08:01) *
В 1989 году на однокристалке 1816ве48 (8048) (7МГц) реализовал ДУ для телевизора. smile.gif Там был плавный программный ШИМ на 4 канала с частотой 100Гц и 64 ступеньки. smile.gif

Александр, это хорошо, можем потом обсудить, но сколько было ступенек для 12 каналов шим, реализованных на си (по ссылке, которую я давал)?
Цитата(SasaVitebsk @ Oct 11 2007, 08:01) *
В принципе число каналов ШИМ практически не влияет на характеристики самого ШИМа. На него влияет только производительность процессора и его аппаратные возможности. При построении программного ШИМа неизбежно сталкиваешься с погрешностями на краях. То есть при слишком малых и при слишком больших значениях. Естественно при высокой частоте ШИМа

Число каналов задаётся разработчиком, как и характеристики шима. Как они могут влиять друг на друга? Что такое погрешности на краях?
Цитата(SasaVitebsk @ Oct 11 2007, 08:01) *
2 GM как ты с этим боролся. Я никак. Просто констатировал ошибку. smile.gif

У меня любой из 24-х каналов шима имеет одинаковые характеристики, т.е. 256 ступенек, длительность каждой ступеньки, скажем, 0.7 мкс (по памяти). Первая ступенька всегда 0, а переход может быть на любой ступеньке, нет никаких краёв, а раз краёв нет, то и борьбы нет(:-).
SasaVitebsk
Тогда мы действительно не понимаем друг друга. smile.gif

Смотря как ты ШИМ реализовывал.
Я реализовывал по прерываниям. Расчитывал как бы таблицу времён-портов. В прерывании перегружал следующую константу таймера и выводил значения портов. В связи с тем, что минимальная разница - одна единица, то длительность прерывания должна быть меньше чем Тшим/Nступеней.

Такое построение имеет своё преимущество. Поскольку я первым делом вывожу состояние портов, то погрешность вывода незначительна. Есть и недостаток. В связи с тем, что прерывание занимает значительное время (вход/выход/сохранение контекста/обработка) то достичь высокой частоты ШИМ не удаётся.

Можно делать прямым опросом таймера, как я видел в примерах на PIC. Честно говоря такой подход меня не устраивает и я его не применяю.

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


Пост о котором я писал я реализовывал первым описанным способом. Управлял ШД (делал дробление) Соответственно полную синусоиду мне выводить не было необходимости. В тоже время точность вывода (число ступенек) была 3200. Что тут непонятного?
=GM=
Цитата(SasaVitebsk @ Oct 11 2007, 22:16) *
Смотря как ты ШИМ реализовывал

Вот моя базовая процедура, выполняется за 18 МЦ, можно еще укоротить (не буду смущать народ, а то придёт сами знаете кто, и начнёт верещать...)
Код
;Процедура формирования 256-точечного 24-канального ШИМа (18МЦ)
pwmisr:  ld   temp,x+       ;обновление
        out   port1,temp    ;порта 1
        ld    temp,x+       ;обновление
        out   port2,temp    ;порта 2
        ld    temp,x+       ;обновление
        out   port3,temp    ;порта 3
        ld    temp,x+       ;новая уставка
        out   TCR0,temp     ;для сравнения
        reti

Как видишь, можно обойтись без всяких сохранений контекстов(:-).

Для твоего случая, для 12 каналов, время прерывания можно сделать порядка 10 МЦ, т.е. с клоком 20 мег время одной ступеньки будет равно 0.5 мкс, период шим = 256*0.5=128 мкс, следовательно частота шим - 7.8 кГц, это на чистом ассемблере, а у тебя 5 кГц на си, неужто компилятор генерит такой быстрый код?
Цитата(SasaVitebsk @ Oct 11 2007, 22:16) *
Пост о котором я писал я реализовывал первым описанным способом. Управлял ШД (делал дробление) Соответственно полную синусоиду мне выводить не было необходимости. В тоже время точность вывода (число ступенек) была 3200. Что тут непонятного?

Непонятно одно. Если 3200 ступеней, то как получить шим 5 кГц? У тебя время одной ступени будет 200 мкс/3200=62.5 нс, а это примерно один МЦ контроллера.
add
Такой вопросик с целью понимания вопроса(тфтsmile.gif): Скажите уважаемые коллеги, если у меня 256 ступенек шима и порт я обновляю с частотой 25кГц, то минимальное (суммарное значение длительности импульсов) за 1с получается 1/256с?так? И могу ли я справедливо назвать что шим 25Кгц?
зы: делал программный 32-ух канальный шим для управления диодами. 25кГц,256дискрет.
adnega
18-канальный ШИМ на Мега8 (18.432МГц) для управлениея 6-тью полноцветными светодиодами

T0_OVF:
push temp
in temp,SREG
push temp
clc
ror rd
ror rd
cp cval,rR5 //PD2
ror rd
cp cval,rB4 //PD3
ror rd
cp cval,rG4 //PD4
ror rd
cp cval,rR4 //PD5
ror rd
cp cval,rB3 //PD6
ror rd
cp cval,rG3 //PD7
ror rd
cp cval,rR3 //PB0
ror rb
cp cval,rB2 //PB1
ror rb
cp cval,rB6 //PB2
ror rb
cp cval,rG2 //PB3
ror rb
cp cval,rG6 //PB4
ror rb
cp cval,rR2 //PB5
ror rb
clc
ror rb
ror rb
cp cval,rR6 //PC0
ror rc
cp cval,rB1 //PC1
ror rc
cp cval,rB5 //PC2
ror rc
cp cval,rG1 //PC3
ror rc
cp cval,rG5 //PC4
ror rc
cp cval,rR1 //PC5
ror rc
clc
ror rc
ror rc
com rb
com rc
com rd
out PORTB,rb
out PORTC,rc
out PORTD,rd
inc cval
andi cval,0x7F ; 127 - уровней, мона убрать - станет 8-бит
pop temp
out SREG,temp
pop temp
reti

Инициализация таймера:
// Настройка таймера T0
clr temp
out TCNT0,temp
ldi temp,(1<<PSR10)
out SFIOR,temp
ldi temp,0x01
out TCCR0,temp
ldi temp,(1<<TOIE0)
out TIMSK,temp
Объявление переменных:
// Константы
.equ F_CPU = 18432000
.equ USART_SPEED = 19200
// Определения
.def rR1 = r1
.def rG1 = r2
.def rB1 = r3
.def rR2 = r4
.def rG2 = r5
.def rB2 = r6
.def rR3 = r7
.def rG3 = r8
.def rB3 = r9
.def rR4 = r10
.def rG4 = r11
.def rB4 = r12
.def rR5 = r13
.def rG5 = r14
.def rB5 = r15
.def rR6 = r16
.def rG6 = r17
.def rB6 = r18
.def temp = r19
.def rb = r20
.def rc = r21
.def rd = r22
.def buf = r23
.def cval = r24
defunct
Цитата(=GM= @ Oct 12 2007, 02:01) *
Код
;Процедура формирования 256-точечного 24-канального ШИМа (18МЦ)
pwmisr:  ld   temp,x+    ;обновление
        out   port1,temp;порта 1
        ld    temp,x+    ;обновление
        out   port2,temp;порта 2
        ld    temp,x+    ;обновление
        out   port3,temp;порта 3
        ld    temp,x+    ;новая уставка
        out   TCR0,temp;для сравнения
        reti

Как видишь, можно обойтись без всяких сохранений контекстов(:-).

Ну ну..
1. теряем ценный регистр X.
2. не понятно где осуществляется сброс X в начальное состояние.
3. почему TCR0 меняется самым последним?
4. Зачем он вообще меняется (25-й канал)?

Функция по тактам возможно и оптимальна, но с т. з. функциональности то, что вы привели - это разрозненные колбасные обрезки.
=GM=
Цитата(add @ Oct 12 2007, 04:44) *
если у меня 256 ступенек шима и порт я обновляю с частотой 25кГц, то минимальное (суммарное значение длительности импульсов) за 1с получается 1/256с?так? И могу ли я справедливо назвать что шим 25Кгц?

Немного не так. Обновление порта может случиться на любой из ступенек, следовательно, частота 256-ступенчатого шима ровно в 256 раз меньше частоты обновления порта, т.е. 97 Гц.
Rst7
Цитата(=GM= @ Oct 12 2007, 02:01) *
Вот моя базовая процедура, выполняется за 18 МЦ, можно еще укоротить (не буду смущать народ, а то придёт сами знаете кто, и начнёт верещать...)
Код
;Процедура формирования 256-точечного 24-канального ШИМа (18МЦ)
pwmisr:  ld   temp,x+      ;обновление
        out   port1,temp;порта 1
        ld    temp,x+      ;обновление
        out   port2,temp;порта 2
        ld    temp,x+      ;обновление
        out   port3,temp;порта 3
        ld    temp,x+      ;новая уставка
        out   TCR0,temp    ;для сравнения
        reti


Это конечно класно. Но вопрос возникает тогда, когда надо подготовить колбаску в озу для выдачи в порт. Это же очень медленно. Т.е. если понадобится изменить заполнение какого-либо канала, то на это нужно потратить очень много времени.
add
Цитата(=GM= @ Oct 12 2007, 13:10) *
Немного не так. Обновление порта может случиться на любой из ступенек, следовательно, частота 256-ступенчатого шима ровно в 256 раз меньше частоты обновления порта, т.е. 97 Гц.

Спасибо! таким образом получается что ШИМ который имеет значение 256 дискрет, и частоту обновления 5Кгц, незя называть 5-ти килогерцовым шимом?wacko.gif
и как тада получается :
Цитата
Спрашиваю потому, что тоже реализовал 24 канала шим с частотой 6 кГц, правда, на ассемблере. Шим был 8-разрядный, т.е. 256 позиций. Клок 20 МГц.

етож 6000*256=1536000 ~1.5МГц! и на 20МГц, да еще и 24 канала?

зы:видел гдето что шим можно и на апаратном уарте сделать. (последовательно данные выдвигаем), но тут уже надо 32байта запихнуть в USART на один дискрет.
=GM=
Цитата(add @ Oct 12 2007, 09:32) *
етож 6000*256=1536000 ~1.5МГц! и на 20МГц, да еще и 24 канала?

Ну, в ваших терминах, это будет частота обновления трёх 8-ми битных портов одновременно, чтобы получить 24 канала. Программка обработки прерывания 24-канальной шим, которую я привёл выше (см.пост #18), выполняется за 18 МЦ (машинных циклов). Один машинный цикл для клока 20 МГц выполняется за 0.05 мкс. Получается обработка прерывания выполняется за 18*0.05=0.9 мкс. То есть за это время обновляются три порта. Частота обновления портов соответственно будет равна 1/0.9=1.1 МГц, ну а частота шим будет в 256 раз ниже, т.е. 1.1/256=4.3 кГц. Никаких чудес, всё по-честному. (Замечу в скобках, что время выполнения можно ещё малёк подсократить, скажем до 13 МЦ, тогда частота 24-канального шима будет порядка 6 кГц.

Цитата(Rst7 @ Oct 12 2007, 08:29) *
Это конечно класно. Но вопрос возникает тогда, когда надо подготовить колбаску в озу для выдачи в порт. Это же очень медленно. Т.е. если понадобится изменить заполнение какого-либо канала, то на это нужно потратить очень много времени

Да, приём исходных данных и подготовка "колбаски" проводится в фоне и занимает относительно много времени, порядка 2500 МЦ, т.е. 125 мкс. Максимальное занятое время процессора, с учётом времени работы прерываний, составит 148 мкс, в то же время период шим равен 166 мкс. Сиё означает, что есть возможность за ОДИН период шима сменить ВСЕ ДВАДЦАТЬ ЧЕТЫРЕ временные уставки, и еще останется свободными 18 мкс (360 МЦ). Смысла менять уставки чаще, чем период шима, я не вижу(:-)

Пока других, более быстрых способов программного формирования многоканальных шим, я не нашёл.
add
Цитата(=GM= @ Oct 12 2007, 15:20) *
Да, приём исходных данных и подготовка "колбаски" проводится в фоне и занимает относительно много времени, порядка 2500 МЦ, т.е. 125 мкс. Максимальное занятое время процессора, с учётом времени работы прерываний, составит 148 мкс, в то же время период шим равен 166 мкс. Сиё означает, что есть возможность за ОДИН период шима сменить ВСЕ ДВАДЦАТЬ ЧЕТЫРЕ временные уставки, и еще останется свободными 18 мкс (360 МЦ). Смысла менять уставки чаще, чем период шима, я не вижу(:-)

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

Ну в Вашем случае вы подготавливаете "колбаску" вне прерывания.. adnega делает это внутри прерывания.. Есть ли в Вашем способе(скажу что способ вообщем то один и тотже) смысл выносить подготовку "колбаски за прерывание"?В таком случае для экономии времени в прерывании прийдется работать с флагами (события по окончанию обновления "колбаски").. и возможен пропуск момента смены значений? в чем выигрыш? может полагаясь на то что данные выводимые в шим обновляются не так часто? тогда какой смыс так "задирать"частоту"шима?
Rst7
Чето я не понял. Как-то это все не стреляет. Общая идея то понятна, и будет работать, но вот цифры не сходятся wink.gif

Цитата(=GM= @ Oct 12 2007, 14:20) *
(Замечу в скобках, что время выполнения можно ещё малёк подсократить, скажем до 13 МЦ, тогда частота 24-канального шима будет порядка 6 кГц.


Пусть будет 13 тактов. И еще надо добавить 4 - на вычитывание вектора и т.д. - внутренние операции.

Итого 17.

Теперь с другой стороны. За 166мкс*20МГц=3320 тактов надо выполнить 256 прерываний. На прерывание 12.97 тактов. Меньше 17. Значит свободного времени у процессора нет совсем. И есть недобор. Почти в 1.5 раза. Непонятно, где-же время на генерацию колбаски?

Надо бы рыбку уменьшить smile.gif
add
Цитата(Rst7 @ Oct 12 2007, 15:45) *
Надо бы рыбку уменьшить smile.gif

biggrin.gif дауж.. т.е. если у Вас GM ("частота обновления портов соответственно будет равна 1/0.9=1.1 МГц" выражаясь моими терминами smile.gif ) то это совершенно не означает что вы можете менять данные с такой скоростью(переписывать регистры новыми значениями) у вас просто не останется времени на это и плюс еще "колбаска". По честному должна быть возможность менять данные (для 256 значений) один раз через 256 прерываний. так? а сколько у вас остается "свободных"тактов (суммарных от 256-ти прерываний)? По 20МЦ-13МЦ=7МЦ (это наверное с учетом 7 циклов на вход/выход в прерывание) 7*256=1792МЦ... хм.. может быть...?! 05.gif
SasaVitebsk
Цитата(=GM= @ Oct 12 2007, 02:01) *
Как видишь, можно обойтись без всяких сохранений контекстов(:-).

Для твоего случая, для 12 каналов, время прерывания можно сделать порядка 10 МЦ, т.е. с клоком 20 мег время одной ступеньки будет равно 0.5 мкс, период шим = 256*0.5=128 мкс, следовательно частота шим - 7.8 кГц, это на чистом ассемблере, а у тебя 5 кГц на си, неужто компилятор генерит такой быстрый код?


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

Цитата
Непонятно одно. Если 3200 ступеней, то как получить шим 5 кГц? У тебя время одной ступени будет 200 мкс/3200=62.5 нс, а это примерно один МЦ контроллера.

А мне непонятно, что именно непонятно Вам. Частота у меня 16000. 16000/3200 = 5кГц. Для двигателей я использую 2 канала на двигатель. Дробление делаю на 16. Сдвиг фаз 90 градусов. То есть мне фактически надо формировать несколько значений. Вот я и написал в первом своём посте, что плавность не нужна а точность 3200. Вы уточнили про точность.

Но принципиально, тем не менее, так тоже можно работать. За период ШИМ необязательно делать вывод 256 раз (при 256 ступеньках) Достаточно 1 раз на один канал +1. В этом случае тоже создаётся соответствующая колбаса, только она короче. А также учитываются случаи совпадений. Ошибки будут при малых значения разницы таймера.

Цитата(add @ Oct 12 2007, 15:35) *
biggrin.gif дауж.. т.е. если у Вас GM ("частота обновления портов соответственно будет равна 1/0.9=1.1 МГц" выражаясь моими терминами smile.gif ) то это совершенно не означает что вы можете менять данные с такой скоростью(переписывать регистры новыми значениями) у вас просто не останется времени на это и плюс еще "колбаска". По честному должна быть возможность менять данные (для 256 значений) один раз через 256 прерываний. так? а сколько у вас остается "свободных"тактов (суммарных от 256-ти прерываний)? По 20МЦ-13МЦ=7МЦ (это наверное с учетом 7 циклов на вход/выход в прерывание) 7*256=1792МЦ... хм.. может быть...?! 05.gif


Вы, кстати, зря смеётесь. Те характеристики, что указывает GM я берусь реализовать и времени на расчёт останется вагон. Могу продемонстрировать. Лишь бы прерывание укладывалось в ступеньку. Время - я конечно заношу так как указывает defunct, хотя это и не обязательно. П/п линейная и ошибку можно высчитать и учесть при расчётах.
Rst7
Цитата
Но принципиально, тем не менее, так тоже можно работать. За период ШИМ необязательно делать вывод 256 раз (при 256 ступеньках) Достаточно 1 раз на один канал +1. В этом случае тоже создаётся соответствующая колбаса, только она короче. А также учитываются случаи совпадений. Ошибки будут при малых значения разницы таймера.


Конечно, алгоритм генерации будет хитрый. Но в худшем случае будет всего N прерываний, где N - количество шимов. А чтобы не было ошибок, надо чтобы частота шима была меньше чем Fcpu/(Ni*256), где Ni - количество тактов на обработку прерывания.
SasaVitebsk
Цитата(Rst7 @ Oct 12 2007, 16:01) *
Конечно, алгоритм генерации будет хитрый. Но в худшем случае будет всего N прерываний, где N - количество шимов. А чтобы не было ошибок, надо чтобы частота шима была меньше чем Fcpu/(Ni*256), где Ni - количество тактов на обработку прерывания.

Совершенно в дырочку. Правда прерывание будет чуть чуть длиннее чем у GM. Кроме того, нет никаких проблем с реализацией такого алгоритма на Си. По крайней мере под IARом. Прерывание пишется на АСМе, а всё остальное на Си.


Чуть-чуть доуточню. Прерываний N+1, где N- число шимов. А частота, будет чуть выше, чем ожидает RST, при правильном написании. Потому, что можно разрешить прерывание ранее чем произойдёт выход из прерывания. Надо внимательно анализировать. В общем случае вложенность не имеет принципиального значения, так как общая занятость процессора в прерываниях будет значительно меньше периода ШИМ. А длительность прерывания определяет только минимальный размер ступеньки.
=GM=
Цитата(Rst7 @ Oct 12 2007, 10:45) *
Чето я не понял. Пусть будет 13 тактов. И еще надо добавить 4 - на вычитывание вектора и т.д. - внутренние операции. Итого 17.
Теперь с другой стороны. За 166мкс*20МГц=3320 тактов надо выполнить 256 прерываний. На прерывание 12.97 тактов. Меньше 17. Значит свободного времени у процессора нет совсем. И есть недобор. Почти в 1.5 раза. Непонятно, где-же время на генерацию колбаски? Надо бы рыбку уменьшить smile.gif

Ну так, прерываний-то всего 24 (по совпадению) + 1 (по переполнению таймера). За период 166 мкс (период шим) потратится в лучшем случае 25*0.05*17=21.25 мкс. Остальное время 166-22=144 мкс, вагон и маленькая тележка времени, 86% всего времени процессора, между прочим, можно пустить на расчёт "колбаски" по вашей терминологии.

Цитата(add @ Oct 12 2007, 10:35) *
Ну в Вашем случае вы подготавливаете "колбаску" вне прерывания.. adnega делает это внутри прерывания.. Есть ли в Вашем способе (скажу что способ вообщем то один и тотже) смысл выносить подготовку "колбаски за прерывание"?В таком случае для экономии времени в прерывании прийдется работать с флагами (события по окончанию обновления "колбаски").. и возможен пропуск момента смены значений? в чем выигрыш? может полагаясь на то что данные выводимые в шим обновляются не так часто? тогда какой смысл так "задирать"частоту"шима?

Способ не один и тот же. Уже ответил, повторюсь. У меня всего максимум 24+1 прерывание на период шима, и только одно прерывание может возникнуть в течение одного кванта. На периоде шима умещается 256 квантов. Хотите увеличить частоту шима, не меняя количество квантов (или "ступеней" по-простому), уменьшайте длительность кванта.

Смена значений происходит во время прерывания от таймера (квант 255). Скажу больше, пока прерывания работают с одной "колбаской" в течение текущего периода шима, фоновая программа готовит другую "колбаску" для следующего периода шима, так они поочерёдно и меняются, всё по-честному.

Подчеркну ещё раз, при данном подходе есть принципиальная возможность менять уставки ВСЕХ шимов для КАЖДОГО периода шим. Другой вопрос (и не такой простой), откуда возьмутся новые 48 байт уставок (2 байта на один канал) за период шима (166 мкс).
add
Цитата(SasaVitebsk @ Oct 12 2007, 16:53) *
Вы, кстати, зря смеётесь. Те характеристики, что указывает GM я берусь реализовать и времени на расчёт останется вагон. Могу продемонстрировать....

Если Вас не затруднит, пожалуйста продемонстрируйте. (не обязательно кодом), можно и даже наверное лучше алгоритмом. А главное хотелось бы увидеть количество МЦ отводимых на каждый шаг.
зы: исх. данные вроде были такие: Такт-20МГц , прерывания 1.5МГц, Шим 6КГц 256 шагов. Ну еще в довесок 24 канала. Жду с нетерпением(это не сарказм). :-)
=GM=
Цитата(adnega @ Oct 12 2007, 07:00) *
18-канальный ШИМ на Мега8 (18.432МГц) для управлениея 6-тью полноцветными светодиодами

У вас, кстати, есть логическая ошибка в алгоритме, вследствие чего могут возникнуть глитчи переменной длительности. Возможно для светодиодов это и пройдёт, но для всяких тиристорных коммутаторов думаю вряд ли, не говоря уж об электроприводах.
defunct
Цитата(=GM= @ Oct 12 2007, 17:21) *
Ну так, прерываний-то всего 24 (по совпадению) + 1 (по переполнению таймера).

В таком случае почему вы меняете TCR0, а не OCR0?
И почему не модифицируется X? 24 * 4 = 90 байт, на авто переполнение расчитывать здесь не получится.
=GM=
Цитата(defunct @ Oct 12 2007, 14:29) *
В таком случае почему вы меняете TCR0, а не OCR0? И почему не модифицируется X? 24 * 4 = 90 байт, на авто переполнение расчитывать здесь не получится

Здесь описка, конечно OCR0, вроде регистра TCR0 нет в авроровской природе. В течение периода шим возникает до 24 прерываний по совпадению, В конце периода шим возникает последнее прерывание, по переполнению, там происходит обнуление портов и с помощью команды lds xl,pwmtable заносится адрес другого буфера. И всё по-новой! И вновь продолжается бой!(:-)

Да вот фрагмент, собственно
Код
;Процедура обработки прерывания таймер0 по переполнению (15МЦ)
eopwm:    ldi      temp,0            ;обнуление
          out      porta,temp        ;порта 1
          out      portb,temp        ;порта 2
          out      portc,temp        ;порта 3
          mov      r3,temp           ;начало шим
          lds      XL,pwmtable       ;адрес текущего буфера уставок
          ld       temp,X+           ;первая уставка
          out      OCR0,temp         ;нового периода шим
          reti                       ;сделано

Первый раз я выложил функцию просто для демонстрации SasaVitebsk, как я организовывал шим. ни в одну из описанных им схем я не вошёл. Да собственно, так, как показано, я не делаю, слишком медленно(:-). Теперь, надеюсь, на все ваши нынешние и предыдущие вопросы ответил?
Цитата(defunct @ Oct 12 2007, 07:59) *
1. теряем ценный регистр X.
2. не понятно где осуществляется сброс X в начальное состояние.
3. почему ОCR0 меняется самым последним?
4. Зачем он вообще меняется (25-й канал)?

Функция по тактам возможно и оптимальна, но с т. з. функциональности то, что вы привели - это разрозненные колбасные обрезки

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

Насчёт ценного регистра. А куда их девать, солить что ли(:-)?
defunct
Цитата(=GM= @ Oct 12 2007, 19:15) *
Функция по тактам практически оптимальна, с учетом выбранного алгоритма. И с точки зрения функциональности тут всё нормально, повторюсь, она была выложена просто для демонстрации одной из реализаций шим, ничего более, не как законченная программа, которую взял, прошил и пользуйся.

Согласен, красивый способ как для чисто вывода многоканалного ШИМа,
остается вопрос смены данных? Получается нетривиальная задача сформировать (фактически упаковать) 24 канала. При изменении значения любого одного канала весь массив придется перестроить. С какой частотой дискретизации у вас обновляется массив?
=GM=
Цитата(defunct @ Oct 12 2007, 21:04) *
Согласен, красивый способ как для чисто вывода многоканалного ШИМа, остается вопрос смены данных? Получается нетривиальная задача сформировать (фактически упаковать) 24 канала. При изменении значения любого одного канала весь массив придется перестроить. С какой частотой дискретизации у вас обновляется массив?

Не совсем уверен, что такое "частота дискретизации" обновления массива(:-).

В течение каждого периода шим, т.е. за 166 мкс (поскольку частота шим 6 кГц), формируется упорядоченная таблица для вывода с помощью приведённой выше программы обработки прерываний по совпадению.

Задача была непростая в реализации, поскольку я никогда не занимался сортировкой. Пришлось освоить, был выбран модифицированный пузырьковый метод. Программа даже в неоптимальном виде полностью выполняется в течение периода шим. Далее адрес начала упорядоченной таблицы записывается в ячейку памяти, откуда её берёт программа обработки прерываний по переполнению и засылает в тот самый пресловутый регистр Х, который по-вашему очень "ценный". Вроде всё.
defunct
Цитата(=GM= @ Oct 13 2007, 00:54) *
Не совсем уверен, что такое "частота дискретизации" обновления массива(:-).

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

Цитата
В течение каждого периода шим, т.е. за 166 мкс (поскольку частота шим 6 кГц), формируется упорядоченная таблица для вывода с помощью приведённой выше программы обработки прерываний по совпадению.

хм.. и 166us хватает на сортировку "пузырьком"?
=GM=
Цитата(defunct @ Oct 12 2007, 22:08) *
имел в виду частоту с которой можно менять значения каналов. т.к. мне почему-то показалась, что она будет ниже частоты ШИМ.
хм.. и 166us хватает на сортировку "пузырьком"?

Ну да, а что тут такого? Щас вот посмотрел метод сортировка-слияние, сортируется 12 и еще 12, потом списки сливаются, так на сортировку 12 уставок уходит 618 МЦ. Ну еще на создание текущих записей 664 МЦ. На круг получается где-то 2200 Мц, т.е. 110 мкс. И всё.

Что касается частоты смены значений каналов, то на практике требуется одна смена на 10+ периодов шим, редко меньше.
SasaVitebsk
Цитата(=GM= @ Oct 12 2007, 19:15) *
Первый раз я выложил функцию просто для демонстрации SasaVitebsk, как я организовывал шим. ни в одну из описанных им схем я не вошёл. Да собственно, так, как показано, я не делаю, слишком медленно(:-). Теперь, надеюсь, на все ваши нынешние и предыдущие вопросы ответил?


В смысле не вошёл - это первый из описанных мной способов. Именно так я реализовывал ШИМ в 89 году. Только регистра OCR в 48 не было, но это тоже не проблема.

Алгоритм прост.
Создаётся структура связанная. Значения портов(порта) и следующее время переключения. Для каждого из прерываний. Одна структура действующая, вторая в это время подготавливается. Так же как это делается при работе с изображением.

При подготовке структуры сначала формируются структура, потом переупорядываются значения по возрастанию значений таймера, потом вычитаются значения из предыдущего интегрального. В смысле высчитывается сколько времени должно пройти от предыдущего прерывания OCR. Здесь же учитывается ошибка выставления времени (для проги приведённой GM у меня и defunct её не будет) и учитывается возможность совпадения двух прерываний(об чём я писал выше). В этом случае одно значение убирается а портам делается OR ну или AND в зависимости от полярности ШИМа. По переполнению меняется структура и взводятся все порты.
Если OCR регистра нет, то всё делается на OCF, правда программа прерывания получается чуть больше потому, что контролируется ещё и колличество прерываний (расчитывается где формировать начало ШИМ).



Принципиально таким способом можно выводить более высокую частоту ШИМа чем расчитанная Rst7. Замечу правильно расчитанная. Ну например вдвое выше. Правда при этом будет искажаться значения бизкие к 1. То есть если величина ШИМ для каналов 1 и 2 отличается на 1, то при выводе значение для канала 2 будет искажено на 1 (то есть они будут отличаться на двойку это зацепит и последующие каналы), но существуют задачи, где это не принципиально или где такая ситуация невозможна (как у меня в варианте с ШД). Именно об это я и спрашивал GM, но он не понял.

Существуют модификации такого алгоритма. Если число каналов не так высоко или они взаимно связаны, то можно убрать расчёты а использовать несколько OCR регистров. В том числе на разных таймерах.
Rst7
После размышлений, я пришел к выводу, что самый быстрый вывод без ошибок для каналов с разностью 1 будет выглядеть так
Код
; Это исключительно сохранение контекста (для использования с компилятором)
    MOVW    R13:R12, R31:R30
    MOVW    R31:R30, R11:R10


    REPT    24
    LD      R15, Z+
    OUT     PORT1, R15
    LD      R15, Z+
    OUT     PORT2, R15
    LD      R15, Z+
    OUT     PORT3, R15
    LD      R15, Z+
    SBRS    R15,7
    RJMP    ENDPWM
    ENDM

                          


ENDPWM
    OUT    OCRx,R15

; Это исключительно восстановление контекста (для использования с компилятором)
    MOVW    R11:R10, R31:R30
    MOVW    R31:R30, R13:R12
    RETI


Таймер устанавливается с максимумом 128 (должно быть два OCR в таймере), и старший бит в новом значении для таймера говорит, склеивать ли прерывания в одно.

Только чуть-чуть ширина может меняться, но ее тоже можно скомпенсировать.

Вот в таком варианте ровно 13 тактов, значит макс. частота будет 6.01 кГц (при тактовой 20МГц).

А, и еще должен быть костылек для длительности больше 128 между переключениями.

ЗЫ: а если 23 шима, то заметно быстрее можно. Однако, с жертвой одного бита порта.
=GM=
Цитата(Rst7 @ Oct 13 2007, 11:26) *
После размышлений, я пришел к выводу, что самый быстрый вывод без ошибок для каналов с разностью 1 будет выглядеть ... Таймер устанавливается с максимумом 128 (должно быть два OCR в таймере), и старший бит в новом значении для таймера говорит, склеивать ли прерывания в одно.

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

Кроме того, вы уже пожертвовали старшим битом порта 3, так что у вас будет уже 23 канала, лучше всё же 24, чтобы три порта были использованы под завязку.
Rst7
Цитата(=GM= @ Oct 13 2007, 16:48) *
В принципе я так и делаю в своей программе. В вашем варианте вы забыли про чтение из буфера и запись в OCR0 следующей уставки.


Ничего я не забыл. Я сразу обрабатываю следующее значение порта, не выходя из обработчика.

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


Цитата
Кроме того, вы уже пожертвовали старшим битом порта 3, так что у вас будет уже 23 канала, лучше всё же 24, чтобы три порта были использованы под завязку.


Пожертвовал я старшим битом уставки таймера. Так что шимов - 24
SasaVitebsk
Как правило обработчик прерывания по OCF получается более громоздким, поэтому вблизи 0 при таком варианте происходят искажения. У меня, к примеру были. Хотя я несколько по другому делал. В принципе у GM будут незначительные искажения. Там у него только вывод портов одним значением, переинициализация таймера, переключение "колбасы". В принципе, наверное близкое значение в пересчёте на такты.

Правда требуется синхронизация работы головы с переключением. В АСМе это осуществляется разрешением прерывания и прямым переходом на обработчик - оттуда RET. Здесь если пользоваться подходом Rst7 тоже можно так сделать. А уже обработчик написать на СИ в виде подпрограммы.
=GM=
Цитата(Rst7 @ Oct 13 2007, 13:54) *
Ничего я не забыл. Пожертвовал я старшим битом уставки таймера. Так что шимов - 24

Прошу пардону, проглядел четвёртый оператор ld, который загружает уставку.
=GM=
Цитата(Rst7 @ Oct 13 2007, 11:26) *
Таймер устанавливается с максимумом 128 (должно быть два OCR в таймере), и старший бит в новом значении для таймера говорит, склеивать ли прерывания в одно.
Только чуть-чуть ширина может меняться, но ее тоже можно скомпенсировать.

Добавлю, таймером можно и не жертвовать. Есть два пути решения проблемы, первый - экстенсивный: переключение всего прерывания на обработку уставок до 0х7F и после 0х80, и второй - интенсивный(:-) - вместо уставки поставить флаг сцепленности двух прерываний, а саму уставку читать позднее (там возникнет ещё одна проблемка, тоже решаемая...), не буду сильно грузить, подумайте, может быть коллективный разум придумает третий, наиболее приемлемый путь.
add
Ув. =GM=, перечитал все посты не один раз, но так и непонял сути Вашего алгоритма. Как я понял Вы заранее подготавливаете данные для вывода в порт а также константу сравнения для таймера. Но если у вас для всех 24-х каналов устанавливается длительность одновременно, как меняется значение в каждом канале? Или у Вас задан массив в котором значение для одного канала задается не в одном байте а в х-отом бите каждого байта, а текущая длительность интервала импульса шима по прерыванию либо добавляется, либо нет к интегралу всего периода?
Поправьте пожалуйста, если чтото не так понял. laughing.gif
rolleyes.gifКакое максимальное кол-во значений ступенек можно в таком случае реализовать? И на сколько МЦ затратно подготовка данных для такого шима?
=GM=
Цитата(add @ Oct 15 2007, 09:23) *
Ув. =GM=, перечитал все посты не один раз, но так и непонял сути Вашего алгоритма. Как я понял Вы заранее подготавливаете данные для вывода в порт а также константу сравнения для таймера. Но если у вас для всех 24-х каналов устанавливается длительность одновременно, как меняется значение в каждом канале? Или у Вас задан массив в котором значение для одного канала задается не в одном байте а в х-отом бите каждого байта, а текущая длительность интервала импульса шима по прерыванию либо добавляется, либо нет к интегралу всего периода? Поправьте пожалуйста, если что-то не так понял.
Какое максимальное кол-во значений ступенек можно в таком случае реализовать? И на сколько МЦ затратно подготовка данных для такого шима?

Ну, если по-честному, то я здесь свой алгоритм и не выставлял, немудрено его не понять. Был выложен фрагмент Саше Витебскому просто для иллюстрации, как я реализовывал свой алгоритм, ничего более. Но потом народ стал спрашивать подробности, я стал отвечать, теперь вроде бы должен нести ответ(:-)

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

1) Будем использовать МК с ОЗУ не менее 1 Кбайта и тактовой частотой 20 МГц, ну и ног должно хватить по крайней мере для 24 выходных шимов. Пусть это будет атмега88.

2) Формирование выходных сигналов 24-канальной шим осуществляется в прерывании таймера0 по сравнению в Output Compare Unit (подробнее см. в doc8025а, с.98). Программа выдачи приведена в моём посте #18. Время выполнения 18 машинных циклов (можно снизить до 13).

3) За период шима может возникнуть максимум 24 прерывания (по числу сигналов шим).

4) Полное время на все прерывания не более 18*24*0.05=21.6 мкс.

Как это всё работает. В начале периода шим обнуляются все 24 шим сигнала и таймер начинает работать с 0. В регистр сравнения OCR0A из текущего буфера запихивается первая уставка, т.е. таймерное время, когда некоему шиму (или нескольким, не важно) необходимо перейти из состояния 0 в состояние 1. При достижении равенства уставки и значения в таймере происходит прерывание OCF0A, начинает работать программа из поста #18, которая выставляет в порты сформированные заранее образы всех ТРЁХ портов. Затем программа записывает в OCR0A вторую уставку и засыпает до следующего прерывания.

В это время фоновая программа подготавливает следующий буфер для следующего периода шим. Буфер состоит из 24 записей. Каждая запись содержит 4 байта. Общий вид таблицы таков
Код
              уставка    образ п.А  образ п.B  образ п.C  
   .db         0x10,     0b0000000, 0b0000000, 0b00000011
   .db         0x20,     0b0000000, 0b0000000, 0b00000111
   .db         0x21,     0b0000000, 0b0000000, 0b00001111
   .db         0x50,     0b0000000, 0b0000000, 0b00011111
   . . . . . . . . . . . . . . . . . . . . . . . . . . .
   .db         0x8F,     0b1011111, 0b1111011, 0b00011111
   .db         0xB9,     0b1111111, 0b1111111, 0b11111111

Как можно видеть, бит образа порта, перешедший из состояния 0 в состояние 1 в N-ой записи, остаётся в этом состоянии до конца таблицы.

Основными задачами фоновой программы (с точки зрения формирования шим) являются приём извне (или вычисление) новых уставок для каждого канала и подготовка на их основе нового буфера. По окончанию подготовки нового буфера адрес нового буфера записывается в ячейку памяти. При нулевом времени таймера этот адрес записывается в регистр Х, как я уже говорил ранее. Далее фоновая программа принимает новые уставки и ждёт начала нового периода шим для подготовки нового буфера.

Ну вот, вгрубе и всё описание.

В завершение, ответы на ваши вопросы. Максимальное количество ступенек – 256, за один период шима (для 6 кГц) фоновая программа успевает учесть в подготовке все 24 уставки (подчеркну, если они будут готовы), об этом тоже писалось ранее. Например, в одном периоде можно установить все сигналы в 1, а в следующем – в 0. На выходе всех каналов будет меандр с Fшим/2. Как видите, можно легко и непринуждённо достичь частоты Найквиста(:-).
singlskv
Цитата(=GM= @ Oct 12 2007, 03:01) *
Вот моя базовая процедура, выполняется за 18 МЦ
Код
;Процедура формирования 256-точечного 24-канального ШИМа (18МЦ)
pwmisr:  ld   temp,x+      ;обновление
        out   port1,temp;порта 1
        ld    temp,x+      ;обновление
        out   port2,temp;порта 2
        ld    temp,x+      ;обновление
        out   port3,temp;порта 3
        ld    temp,x+      ;новая уставка
        out   TCR0,temp    ;для сравнения
        reti
=GM=, лихо у Вас такты подсчитаны, я так не умею biggrin.gif
Вот мой расчет для худшего случая:
3(джиттер при попадании прерывания на 4 тактовую команду) + 4(вход в прерывание) +
4*2(LD) + 4*1(OUT) + 4(RETI) + 4(максимальное количество тактов до начала следующего прерывания) = 27 тактов однако...
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.