Осиливаем ШИМ в теплой компании Arduino и RGB светодиода. Управление светодиодной лентой при помощи Аrduino Управление цветом и яркостью rgb светодиодами ардуино

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



RGB-светодиод (аббревиатура означает RED, GREEN, BLUE) является сочетанием кристаллов, способных генерировать красный, зеленый и синий цвета. Благодаря такому сочетанию данные светодиоды могут воспроизводить 16 миллионов оттенков света. Управлять RGB-светодиодами несложно, и они без проблем могут использоваться в проектах с Arduino. В данном материале будет показан пример управления RGB-светодиодом с помощью Arduino.


Поскольку RGB-светодиод, как было отмечено выше, является сочетанием кристаллов трех разных базовых цветов, то схемотехнически он изображается как три светодиода. Конструктивно такой светодиод имеет один общий вывод и три вывода для каждого цвета. Ниже показана схема подключения RGB-светодиода к Arduino. Также на схеме имеется буквенно-числовой ЖК-дисплей 16×2, потенциометры и последовательно соединенные с линиями RGB-светодиода резисторы. Эти резисторы (R1 = 100 Ом, R2 = 270 Ом, R3 = 330 Ом) ограничивают ток светодиодов, чтобы они не вышли из строя. Переменные резисторы (потенциометры) VR1-VR3 сопротивлением 10 КОм используются для управления интенсивностью свечения RGB-светодиода, то есть с помощью них можно задавать цвет светодиода, меняя интенсивность красного, зеленого и синего кристаллов. Потенциометр VR1 соединен с аналоговым входом A0, VR2 с аналоговым входом A1, а VR3 с аналоговым входом A2.



ЖК-дисплей в данном случае используется для отображения значения цвета и шестнадцатеричного значения цветового кода. Значение цветового кода отображается в 1-й строке ЖК-дисплея (в виде Rxxx Gxxx Bxxx, где xxx представляет собой числовое значение), а шестнадцатеричный код отображается во 2-й строке ЖК-дисплея (в виде HEXxxxxxx). Резистор R4 сопротивлением 100 Ом применяется для ограничения тока, прикладываемого к подсветке ЖК-дисплея, а для регулировки контрастности ЖК-дисплея используется переменный резистор VR4 сопротивлением 10 КОм.


Ниже приведен код (скетч), позволяющий управлять изменением цвета RGB-светодиода с помощью платы Arduino и подключенными к ней потенциометрами.


#include // библиотека для ЖК-дисплея LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // линии Arduino для подключения ЖК-дисплея int Radj; int Gadj; int Badj; int Rval=0; int Gval=0; int Bval=0; int R = 9; int G = 10; int B = 11; void setup() { pinMode(R, OUTPUT); // Линия 9 направлена на выход pinMode(G, OUTPUT); // Линия 10 направлена на выход pinMode(B, OUTPUT); // Линия 11 направлена на выход lcd.begin(16,2); // Инициализация дисплея delay(1); lcd.setCursor(0,0); lcd.print("RGB COLOUR"); lcd.setCursor(4,1); lcd.print("GENERATOR"); delay(2000); lcd.setCursor(0, 0); lcd.print(" R G B "); lcd.setCursor(3,1); lcd.print("HEX= "); } void loop() { Radj = analogRead(0); Gadj = analogRead(1); Badj = analogRead(2); Rval=Radj/4; // Convert the range from (0-1023) to (0-255) Gval=Gadj/4; // Convert the range from (0-1023) to (0-255) Bval=Badj/4; // Convert the range from (0-1023) to (0-255) lcd.setCursor(2,0); if (Rval<10) { lcd.setCursor(2,0); lcd.print("00"); lcd.print(Rval); } else if(Rval<100) { lcd.setCursor(2,0); lcd.print("0"); lcd.print(Rval); } else { lcd.setCursor(2,0); lcd.print(Rval); } lcd.setCursor(8,1); if (Rval<16) { lcd.print("0"); lcd.print(Rval, 16); } else { lcd.print(Rval, 16); } lcd.setCursor(7,0); if (Gval<10) { lcd.setCursor(7,0); lcd.print("00"); lcd.print(Gval); } else if(Gval<100) { lcd.setCursor(7,0); lcd.print("0"); lcd.print(Gval); } else { lcd.setCursor(7,0); lcd.print(Gval); } lcd.setCursor(10,1); if (Gval<16) { lcd.print("0"); lcd.print(Gval, 16); } else { lcd.print(Gval, 16); } lcd.setCursor(12,0); if (Bval<10) { lcd.setCursor(12,0); lcd.print("00"); lcd.print(Bval); } else if(Bval<100) { lcd.setCursor(12,0); lcd.print("0"); lcd.print(Bval); } else { lcd.setCursor(12,0); lcd.print(Bval); } lcd.setCursor(12,1); if (Bval<16) { lcd.print("0"); lcd.print(Bval, 16); } else { lcd.print(Bval, 16); } analogWrite(R, Rval); // ШИМ-выход для красного цвета analogWrite(G, Gval); // ШИМ-выход для зеленого цвета analogWrite(B, Bval); // ШИМ-выход для синего цвета }

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

Трехцветный светодиод (rgb led) — это три светодиода разных цветов в одном корпусе. Они бывают как с небольшой печатной платой, на которой расположены резисторы, так и без встроенных резисторов. Мы рассмотрим оба варианта.

Видео-инструкция сборки модели Arduino с трехцветным светодиодом:

Для сборки модели с трехцветным светодиодом нам потребуется:

  • плата Arduino
  • программа Arduino IDE, которую можно скачать с сайта Arduino .

Что потребуется для Arduino с трехцветным светодиодом со встроенными резисторами?

Если используется светодиод без резисторов, нам также потребуется:

  • Breadboard
  • 4 провода “папа-папа”
  • 3 резистора на 220 Ом

Что потребуется для Arduino с трехцветным светодиодом без встроенных резисторов

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

Схема подключения модели Arduino с трехцветным светодиодом со встроенными резисторами:

Схема подлючения трехцветным светодиодом со встроенными резисторами

Схема подключения модели Arduino с трехцветным светодиодом без встроенных резисторов:

Схема подлючения трехцветным светодиодом без встроенных резисторов

Для управления этой моделью подойдет следующая программа (программу вы можете просто скопировать в Arduino IDE):

//объявляем переменные с номерами пинов
int r = 13;
int g = 12;
int b = 11;
void setup() //процедура setup
{
//объявляем используемые порты
pinMode(r, OUTPUT);
pinMode(g, OUTPUT);
pinMode(b, OUTPUT);
}
void loop() //процедура loop
{
digitalWrite(r, HIGH); //включаем красный
delay(500); //ждем 500 Мс
digitalWrite(r, LOW); //выключаем красный
digitalWrite(g, HIGH); //включаем зеленый
delay(500); //ждем 500 Мс
digitalWrite(g, LOW); //выключаем зеленый
digitalWrite(b, HIGH); //включаем синий
delay(500); //ждем 500 Мс
digitalWrite(b, LOW); //выключаем синий
}

Так выглядит собранная модель Arduino с трехцветным светодиодом без выстроенных резисторов:

Собранная модель Arduino с трехцветным светодиодом без встроенных резисторов

Продолжение следует!

Посты по урокам:

  1. Первый урок:
  2. Второй урок:
  3. Третий урок:
  4. Четвертый урок:
  5. Пятый урок:
  6. Шестой урок:
  7. Седьмой урок:
  8. Восьмой урок:
  9. Девятый урок:

Широтно-импульсная модуляция (ШИМ, PWM) - веселая штука, и особенно прикольно с ее помощью управлять сервомоторами, однако сегодня мы применим ее к трехцветному светодиоду. Это позволит нам управлять его цветом и получить некое подобие красоты.

ШИМ

Гениально определение ШИМ сформулировано в Википедии , поэтому я просто скопипащу его оттуда: "ШИМ - приближение желаемого сигнала (многоуровневого или непрерывного) к действительным бинарным сигналам (с двумя уровнями - вкл / выкл ), так, что, в среднем, за некоторый отрезок времени, их значения равны. <...> ШИМ есть импульсный сигнал постоянной частоты и переменной скважности, то есть отношения периода следования импульса к его длительности. С помощью задания скважности (длительности импульсов) можно менять среднее напряжение на выходе ШИМ . "


Теперь разберемся, что это значит. Пусть есть обычный такой прямоугольный сигнал:




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


Если у нас максимальное напряжение равно 5 В, то напряжение, получаемое на выходе ШИМ равно скважность умножить на 5 В (и делить на 100% чтобы формал-nazi не привязывались):


Arduino позволяет записать на ШИМ-выход значение от 0 до 255, а это значит, что мы можем получить напряжение с дискретностью примерно 20 мВ.


Трехцветный светодиод

Вот он, четырехногий красавец:


Самая длинная нога - это общий анод, а все остальные - это катоды, каждый отвечает за свой цвет: (смотрим на рисунок) самая нижняя - красный, вторая сверху - зеленый, самая верхняя - синий.

Если подать на длинную ногу +5В, а на все остальные 0В, то получится белый свет (умоляю, предохраняйтесь - ставьте ограничивающие резисторы!). Насколько он белый, можно судить по следующему видео:


Но получать белый цвет на нем как раз-таки неинтересно. Посмотрим, как заставить его переливаться разными цветами.

ШИМ на Arduino

Частота ШИМ на Arduino - примерно 490 Гц. На плате Arduino UNO выводы, которые могут быть использованы для ШИМ - 3,5,6, 9, 10 и 11. На плате к этому есть подсказка - шелкографией перед номерами ШИМ-выводов есть тильда или диез.

Нет ничего проще, чем управлять ШИМ на Arduino! Для этого используется одна единственная функция analogWrite(pin, value) , где pin - номер вывода, а value - значение от 0 до 255. При этом ничего не надо писать в void setup() !

Подробнее про это на английском языке можно почитать и .

Совсем немного работаем

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

Соберем незамысловатую схему:


И напишем незамысловатый код:

//обзываем выводы соответственно цвету
int REDpin = 9;
int GREENpin = 10;
int BLUEpin = 11;

void setup (){}

void loop (){
for (int value = 0 ; value <= 255; value +=1) {
//яркость красного уменьшается
analogWrite (REDpin, value);
//яркость зеленого увеличивается
analogWrite (GREENpin, 255-value);
//синий не горит
analogWrite (BLUEpin, 255);
//пауза
delay (30);
}

for (int value = 0 ; value <= 255; value +=1) {
//красный не горит
analogWrite (REDpin, 255);
//яркость зеленого уменьшается
analogWrite (GREENpin, value);
//яркость синего увеличивается
analogWrite (BLUEpin, 255-value);
//пауза
delay (30);
}

for (int value = 0 ; value <= 255; value +=1) {
//яркость красного увеличивается
analogWrite (REDpin, 255-value);
//зеленый не горит
analogWrite (GREENpin, 255);
//яркость синего уменьшается
analogWrite (BLUEpin, value);
//пауза
delay (30);
}
}

Недавно я купил 2 рулона rgb светодиодной ленты по 5 метров, и сразу решил поэкспериментировать с ней. В комплекте шёл блок для управления лентой, но мне показались программы малоинтересными, а под рукой был Tinyos Uno (полный китайский аналог Arduino UNO ). Учитывая то, что ардуино работает максимум с 5в на выход, а чаще 3.3в то нужно было найти решение. В этой статье рассмотрено как подключить 12 вольтовую светодиодную ленту к ардуино с отдельным питанием для ленты и управлением с ардуинки с использованием МОСФЕТов (MOSFETs) для управления каналами RGB ленты .

Начнём со стандартной спецификации аналоговой светодиодной ленты, которая, как мне кажется, наиболее распространена.

  • 10.5mm (0.41") ширина, 3mm (0.12") толщина, 100mm (3.95") длина на каждый сегмент
  • Бывает влагозащищённые (waterproof ) и без защиты (non-waterproof )
  • Имеет клейкую ленту с обратной стороны для крепления ленты
  • Максимальное напряжение 12V и 60mA на каждый сегмент
  • 3 светодиода с общим анодом на каждом сегменте
  • Длина волн светодиодов: 630nm/530nm/475nm
  • Нет микроконтроллера или чип контроллера (чисто аналоговое управление)

Прежде чем начинать работу с Arduino и напряжением выше, чем 5 вольт очень советую прочитать про , чтобы не сжечь вашу Arduino . В ней хорошо описано использование МОСФЕТов с Ардуино . Если вы уже прочитали эту статью, то давайте двигаться дальше.
Для работы со светодиодной лентой нам понадобятся:

  1. MOSFET транзисторы, которые можно найти на том же EBay
  2. Резисторы на 10k?
  3. Макетная беспаечная плата (breadboard )
  4. Проводки для соединения ардуино с макетной платой (папа-папа)
  5. Светодиодная лента (Я брал на aliexpress вот эту ленту , и планирую заказать ещё пару у этого же продавца)

Теперь перейдём к схеме подключения, из неё станет понятно основная часть данной статьи:

Теперь обратимся к Arduino IDE , в которой напишем скетч для управления нашей лентой:

// Note that there"s some legacy code left in here which seems to do nothing // but should do no harm ... // don"t futz with these, illicit sums later #define RED 9// pin for red LED #define GREEN 10 // pin for green - never explicitly referenced #define BLUE 11 // pin for blue - never explicitly referenced #define SIZE 255 #define DELAY 20 #define HUE_MAX 6.0 #define HUE_DELTA 0.01 //long deltas = { 5, 6, 7 }; long rgb; long rgbval; // for reasons unknown, if value !=0, the LED doesn"t light. Hmm ... // and saturation seems to be inverted float hue=0.0, saturation=1, value=1; /* chosen LED SparkFun sku: COM-09264 has Max Luminosity (RGB): (2800, 6500, 1200)mcd so we normalize them all to 1200 mcd - R 250/600 = 107/256 G 250/950 = 67/256 B 250/250 = 256/256 */ long bright = { 107, 67, 256}; //long bright = { 256, 256, 256}; long k, temp_value; void setup () { randomSeed(analogRead(4)); for (k=0; k<3; k++) { pinMode(RED + k, OUTPUT); rgb[k]=0; analogWrite(RED + k, rgb[k] * bright[k]/256); } } void loop() { hue += HUE_DELTA; if (hue > HUE_MAX) { hue=0.0; } rgbval=HSV_to_RGB(hue, saturation, value); rgb = (rgbval & 0x00FF0000) >> 16; // there must be better ways rgb = (rgbval & 0x0000FF00) >> <3; k++) { // for all three colours analogWrite(RED + k, rgb[k] * bright[k]/256); } delay(DELAY); } long HSV_to_RGB(float h, float s, float v) { // H is given on . S and V are given on . // RGB is returned as a 24-bit long #rrggbb int i; float m, n, f; // not very elegant way of dealing with out of range: return black if ((s<0.0) || (s>1.0) || (v<1.0) || (v> < 0.0) || (h > 6.0)) { return long(v * 255) + long(v * 255) * 256 + long(v * 255) * 65536; } i = floor(h); f = h - i; if (!(i&1)) { f = 1 - f; // if i is even } m = v * (1 - s); n = v * (1 - s * f); switch (i) { case 6: case 0: return long(v * 255) * 65536 + long(n * 255) * 256 + long(m * 255); case 1: return long(n * 255) * 65536 + long(v * 255) * 256 + long(m * 255); case 2: return long(m * 255) * 65536 + long(v * 255) * 256 + long(n * 255); case 3: return long(m * 255) * 65536 + long(n * 255) * 256 + long(v * 255); case 4: return long(n * 255) * 65536 + long(m * 255) * 256 + long(v * 255); case 5: return long(v * 255) * 65536 + long(m * 255) * 256 + long(n * 255); } }

Заливаем скетч на ардуино и радуемся.
Есть так же вариант с использованием температурного сенсора DS18B20 , который работает по протоколу 1-Wire .

Идея состоит в том, что "холодная температура", как правило, синего цвета, а "горячая температура " получит красный цвет. Если вы взгляните на колесо HSV, которое мы использовали в первом скетче (цветовое колесо см. wiki), цвет будет составлять от 240 °, когда холодно и до 0 °, если горячая, двигаясь по часовой стрелке (проходя голубой, зеленый и желтый).

Холодная температура в данном случае имеется ввиду 18 ° C, а горячая подразумевает 30 ° C. Температура ниже холодного порога будет считаться холодной, выше горячего порога - горячей. Окончательный код на самом деле довольно прост, когда у вас есть уже готовый код HSV:

// HSV fade/bounce for Arduino // Note that there"s some legacy code left in here which seems to do nothing // but should do no harm ... #include "OneWire.h" //#include "Streaming.h" const int DS18S20_Pin = 2; //DS18S20 Signal pin on digital 2 #define MIN_TEMP 18 #define MAX_TEMP 30 //Temperature chip i/o OneWire ds(DS18S20_Pin); // on digital pin 2 // don"t futz with these, illicit sums later #define RED 9// pin for red LED #define GREEN 10 // pin for green - never explicitly referenced #define BLUE 11 // pin for blue - never explicitly referenced #define SIZE 255 #define DELAY 0 #define HUE_MAX 6.0 #define HUE_DELTA 0.01 //long deltas = { 5, 6, 7 }; long rgb; long rgbval; // for reasons unknown, if value !=0, the LED doesn"t light. Hmm ... // and saturation seems to be inverted float hue=0.0, saturation=1, value=1; /* chosen LED SparkFun sku: COM-09264 has Max Luminosity (RGB): (2800, 6500, 1200)mcd so we normalize them all to 1200 mcd - R 250/600 = 107/256 G 250/950 = 67/256 B 250/250 = 256/256 */ long bright = { 107, 67, 256}; //long bright = { 256, 256, 256}; long k, temp_value; void setup () { randomSeed(analogRead(4)); Serial.begin(57600); for (k=0; k<3; k++) { pinMode(RED + k, OUTPUT); rgb[k]=0; analogWrite(RED + k, rgb[k] * bright[k]/256); } } void loop() { float temperature = constrain(getTemp(), MIN_TEMP, MAX_TEMP); float deltaTemp = (MAX_TEMP - MIN_TEMP); float deltaHue = 4 - 0; hue = map((temperature - MIN_TEMP) * 100, 0, deltaTemp * 100, deltaHue * 100, 0) / 100.0; //Serial << "Temperature: " << temperature << endl; //Serial << "HUE: " << hue << endl; rgbval=HSV_to_RGB(hue, saturation, value); rgb = (rgbval & 0x00FF0000) >> 16; // there must be better ways rgb = (rgbval & 0x0000FF00) >> 8; rgb = rgbval & 0x000000FF; for (k=0; k<3; k++) { // for all three colours analogWrite(RED + k, rgb[k] * bright[k]/256); } //delay(DELAY); } float getTemp(){ //returns the temperature from one DS18S20 in DEG Celsius byte data; byte addr; if (!ds.search(addr)) { //no more sensors on chain, reset search ds.reset_search(); return -1000; } if (OneWire::crc8(addr, 7) != addr) { Serial.println("CRC is not valid!"); return -1000; } if (addr != 0x10 && addr != 0x28) { Serial.print("Device is not recognized"); return -1000; } ds.reset(); ds.select(addr); ds.write(0x44,1); // start conversion, with parasite power on at the end byte present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad for (int i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); } ds.reset_search(); byte MSB = data; byte LSB = data; float tempRead = ((MSB << 8) | LSB); //using two"s compliment float TemperatureSum = tempRead / 16; return TemperatureSum; } long HSV_to_RGB(float h, float s, float v) { // H is given on . S and V are given on . // RGB is returned as a 24-bit long #rrggbb int i; float m, n, f; // not very elegant way of dealing with out of range: return black if ((s<0.0) || (s>1.0) || (v<1.0) || (v>1.0)) { return 0L; } if ((h < 0.0) || (h > 6.0)) { return long(v * 255) + long(v * 255) * 256 + long(v * 255) * 65536; } i = floor(h); f = h - i; if (!(i&1)) { f = 1 - f; // if i is even } m = v * (1 - s); n = v * (1 - s * f); switch (i) { case 6: case 0: return long(v * 255) * 65536 + long(n * 255) * 256 + long(m * 255); case 1: return long(n * 255) * 65536 + long(v * 255) * 256 + long(m * 255); case 2: return long(m * 255) * 65536 + long(v * 255) * 256 + long(n * 255); case 3: return long(m * 255) * 65536 + long(n * 255) * 256 + long(v * 255); case 4: return long(n * 255) * 65536 + long(m * 255) * 256 + long(v * 255); case 5: return long(v * 255) * 65536 + long(m * 255) * 256 + long(n * 255); } }

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

Давайте сделаем наш собственный светодиод RGB из трех отдельных 5-миллиметровых светодиодов. Эти три светодиода имеют прозрачные неокрашенные линзы, поэтому нам нужно светодиоды с прозрачными, бесцветными линзами могут быть любого цвета! Если вы используете другой комплект, просто найдите один красный, один зеленый и один синий светодиод (с бесцветной или окрашенной линзой).

Отключите USB-кабель и замените красный светодиод на один из светодиодов с прозрачной линзой, затем снова подключите USB-кабель.


Какого цвета светодиод? Если вы обнаружите красный с первой попытки, отложите его и повторите процесс, чтобы определить цвет двух других светодиодов.

Подключите два других светодиода с последовательно включенными резисторами на 1K к контактам 10 и 11, как показано на схеме. Загрузите и откройте код из модуля Tinkercad Circuits или скопируйте его и вставьте в новый пустой эскиз Arduino. Загрузите его в свою плату Arduino Uno и посмотрите, можете ли вы сопоставить строки кода с событиями, которые вы видите на светодиодах, как это было сделано нами ранее.

Незнакомой частью этого кода является функция setColor () ; . Это пользовательская функция, определенная в коде после функции void loop() .

void setColor(int red, int green, int blue)
{
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
}

Определение функции включает в себя объявление имени и типа аргументов, которые вы можете использовать в качестве настраиваемых параметров. Эти параметры вы можете изменить каждый раз при выполнении кода. В этой простой функции три целых значения записываются в три светодиодных вывода, используя уже знакомую нам функцию
analogWrite () ; .
setColor(255, 255, 0); // yellow

Каждый раз, когда в основном цикле вызывается эта функция, программа выполняет код в функции перед тем, как продолжить выполнение основного цикла. В этом случае аргументы используются в качестве кода уровня яркости каждого из светодиодов. Диапазон для установки яркости составляет 0-255, так как для управления каждым цветом используется один байт, что позволяет использовать 256 отдельных уровней яркости.

Теперь загрузите и откройте код из этого более сложного проекта RGB или скопируйте и вставьте код в новый пустой эскиз Arduino. Прочтите комментарии в коде, чтобы больше узнать о том, как работает эта программа. Код делает некоторые математические вычисления для преобразования диапазона 0-100 в нужный диапазон, который требуется светодиодам (0-255). Это удобно, так как вы можете думать о яркости в процентах вместо диапазона 0-255.

Для хранения информации о цвете, код использует набор переменных, называемый

2024 bioaquansk.ru. Школа компьютерной грамотности.