LED индикатор загруженности ЦП

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

Эта статья является логическим продолжением для применения кода, описанного в третьем видеоуроке о mikroPascal.

Исходя из того, что у меня нет лазерного принтера (ЛУТ отпадает), двухъядерный процессор и один светодиод стоит примерно 0.8 гривны, было решено делать 3 строки светодиодов по 10  штук в каждой. Две — под индикацию загрузки процессора, и оставшаяся под память или третье ядро (не сталкивался, но слышал).

Основная информация об устройстве:

  1. Напряжение питания: 5 V
  2. Потребляемый ток: < 50 mA 
  3. Частота обновления данных: 0,5

Теперь можно перейти к схеме. 

Как видите, она достаточно простая. Основа схемы — микроконтроллер ATmega8 (в корпусе DIP-28). Общее количество деталей — 40 шт., из них : светодиодов — 31, транзисторов — 5, остальное микроконтроллер и обвязка генератора.

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

Рис.2 (схема включения светодиодов).

Таким способом мне удалось «уложиться» в два порта МК (PORTB — 6 пин, PORTC — 5 пин). Можно было конечно использовать сдвиговые регистры, но это повлекло бы за собой усложнение программы, а так же занятое место на плате. По-этому решил обойтись без них. 

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

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

Как видите, разводка платы не самая простая. В этом виновата прежде всего динамическая индикация. Из-за нее пришлось часть проводников перенести на обратную сторону платы, а так же использовать большое количество переходных отверстий (в основном  для подключения светодиодов). Но, тем не менее эту плату возможно сделать и без ЛУТа. И последнее относительно платы — я использовал текстолит толщиной 0,5 мм, так как другого с двухсторонним покрытием в наличии не оказалось.

Рассмотрев аппаратную часть можно перейти к программной.

Программу для МК я как всегда писал в среде mikroPascal for AVR (v 6.01). Так как в микропаскале нет библиотеки для микроконтроллера Atmega8, которая позволяет работать с USB, то выбор пал на UART. Но так как последний видеоурок по mikroPascal был про прерывания, то решил и сюда их всунуть. И вот что в итоге получилось:

 program iCPU_t;  uses AADL;  var dat:array [0..2] of byte;     rec,i,c,enbl:byte;     rcit,rci,ti:integer;       const pc: array [0..10] of byte = (31,30,28,24,16,0,30,28,24,16,0);             //Объявляем константы const pb0: array [0..10] of byte = (0,254,254,254,254,254,253,253,253,253,253); //Они будут использоваться, как вы уже поняли из const pb1: array [0..10] of byte = (0,251,251,251,251,251,247,247,247,247,247); //из названий, для отправки заданных значений const pb2: array [0..10] of byte = (0,239,239,239,239,239,223,223,223,223,223); //в порт.    procedure usart_c(); iv IVT_ADDR_USART__RXC;                                    //Функция, которая вызывается по прерыванию UART begin                                                                           //(если в буфере приема есть данные).      rec:=UART_Read();                                                          //      if rec=47 then enbl:=1;                                                    //Тут мы ищем нужные нам символы, и сразу их      if enbl=1 then inc(enbl) else                                              //переводим.      if enbl=2 then begin                                                       //      case rec of                                                                //      48: rec:=0;      49: rec:=1;      50: rec:=2;      51: rec:=3;      52: rec:=4;      53: rec:=5;      54: rec:=6;      55: rec:=7;      56: rec:=8;      57: rec:=9;      end;      ti:=rec;      inc(i);      case i of      1: rci:=ti*10;      2: rci:=rci+(ti);      end;      if i>=2 then begin i:=0; enbl:=0; end;      end; end;   begin Uart1_init(9600);                                                               //Инициализация UART  UCSRB.B7:=1;                                                                    //Прерывание по приему данных UART SREG_I_bit:=1;                                                                  //Разрешаем прерывания   DDRC:=0xFF; DDRB:=0xFF; DDD7_bit:=1;  PORTD7_bit:=0;  for c:=0 to 2 do dat[c]:=0;  While TRUE do begin         rcit:=rci;         if (rcit>30) and (rcit<40) then dat[2]:=rcit-30;                        //Присваиваем значение различным         if (rcit>20) and (rcit<30) then dat[1]:=rcit-20;                        //элементам массива dat, в зависимости от того,         if (rcit>10) and (rcit<20) then dat[0]:=rcit-10;                        //какому ядру они соответствуют.              for c:=0 to 2 do begin                  case c of                       0: if dat[c]<=5 then begin                                //Дальше обеспечиваем индикацию.                             PORTC:=pc[dat[c]];                             PORTB:=pb0[dat[c]];                             delay_ms(1);                          end else begin                              PORTC:=pc[5];                              PORTB:=pb0[dat[c]-5];                              delay_ms(1);                              PORTC:=pc[dat[c]];                              PORTB:=pb0[dat[c]];                              delay_ms(1);                          end;                       1: if dat[c]<=5 then begin                             PORTC:=pc[dat[c]];                             PORTB:=pb1[dat[c]];                             delay_ms(1);                          end else begin                              PORTC:=pc[5];                              PORTB:=pb1[dat[c]-5];                              delay_ms(1);                              PORTC:=pc[dat[c]];                              PORTB:=pb1[dat[c]];                              delay_ms(1);                          end;                       2: if dat[c]<=5 then begin                             PORTC:=pc[dat[c]];                             PORTB:=pb2[dat[c]];                             delay_ms(1);                          end else begin                              PORTC:=pc[5];                              PORTB:=pb2[dat[c]-5];                              delay_ms(1);                              PORTC:=pc[dat[c]];                              PORTB:=pb2[dat[c]];                              delay_ms(1);                          end;                  end;              end;     end; end.

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

  1. Анализируем принятые данные, если они соответствуют «/», то разрешаем в следующий раз программе пройти дальше.
  2. Расшифровываем из ANCII кодировки.
  3. Рассчитываем и записываем в переменную

Далее, в основном цикле программы мы сначала сопоставляем, к какому ядру относятся полученные данные, а потом их присваиваем соответствующему элементу массива dat. 

Вот и весь алгоритм программы.

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

  1. Смотрим, какая строка сейчас должна обновиться.
  2. Выбираем из массива dat нужный элемент.
  3. Если нужно зажечь пять или меньше светодиодов в строке, то просто оправляем значение в порт. Если же больше пяти (число n), то сначала зажигаем первые пять, а потом то что осталось (n-5). Все это крутится в цикле.

Из фузов нужно выставить только внешний кварц (8 MHz).

В Khazama AVR Programmer это выглядит следующим образом:

Рис. 4(фузы).

Вот и все! Но если у вас нет каких-то деталей, но есть их аналоги…. Светодиоды можно взять любые, подходящие по току и размерам. Микроконтроллер заменить нельзя. Только взять такой же но с любым другим индексом (например, вместо ATmega8 — ATmega8L и т.д.). Транзисторы так же можно взять любые маломощные структуры p-n-p. Я использовал КТ361Г (работают нормально, хотя еще 1987 г. выпуска 🙂 ). Резисторы — с разбросом от указанного номинала до +/- 20 %.

И напоследок, о программе для Windows, которая управляет индикацией. Она написана в Delphi xe5 и называется iCPU. 

Рис.5(iCPU).

Рассчитана на 2 ядра. В настройках выбирается порт, к которому подключено устройство. Возможно использовать как встроенный COM порт (через преобразователь уровней на MAX232), так и через виртуальный COM порт, с использованием переходника на базе PL2303 и подобных. У меня в городе можно купить такой кабель для старых моделей NOKIA.

Рис.6(кабель).

Внутри платка, которая с успехом может использоваться как USB-COM переходник.

Вот несколько фотографий готового устройства.

Обновление от 20.07.2014:
Доработал программу, теперь можно выводить на 3-ю строку загруженность памяти. А так же мелкие исправления.

Обновление от 01.05.2015:

Полностью переписан код как прошивки для МК, так и программы для Windows. Теперь есть отдельная версия для Windows x64.

Для МК прошивка писалась в WinAVR (Cpp).

ВНИМАНИЕ! Старая прошивка + новая версия программы НЕСОВМЕСТИМЫ, как и новая прошивка + старая версия программы.

При прошивке фузы указывать те же, что и для старой версии, за исключением (выставлять как на скрине):

.

Из «нововведений»:

  • Возможность применять «выравнивание» (по левому краю, по центру и по правому краю).
  • Автоматическое отключение индикации через 10с (например, если комп погрузили «в сон»).
  • Теперь есть «тестовый» режим, в котором включаются все светодиоды (например, при первых включениях уст-ва, для выявления неполадок).
  • Если у вас в системе многоядерный процессор, то будут отображаться только первые 2 ядра (на уст-ве, в программе будет видно загрузку всех ядер). В противном случае, на уст-ве будут активными только 1-я и 3-я строки (1 ядро и память соответственно).

Так же хочу заметить, что перед первым запуском программы, впишите номер ком порта, к которому подключено устройство, в файл «settings.ini». Он лежит в папке с exe-шником. Иначе, скорее всего, будете через каждые 0.5с получать окна с ошибками!

В аппаратную часть устройства никакие правки не вносились.

Немного фотографий:

Обновление от 16.05.2015:

Оптимизирован код. Добавлена возможность регулировки яркости (программный ШИМ на таймере T2). Внесены некоторые правки в программу для Windows (теперь, если COM порт выбран неверно (порта с таким номером в системе нет), не будет +100500 окон с ошибками).

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
U1 МК AVR 8-бит ATmega8 1 DIP-28
D1-D30 Светодиод HL-A-3528H308W-S1-13 30 Любые светодиоды 0805, подходящие по току
C1, C2 Конденсатор 27 пФ 2 0805
X1 Кварцевый резонатор 8 МГц 1 Низкопрофильный
R2-R6 Резистор 1 кОм 5 0805
R1, R7-R10 Резистор 30 кОм 5 0805
Q1-Q4 Транзистор 2N3702 5 Или КТ361