Я достиг зияющих высот! :-)
Во-первых у меня таки заработала полноценно плата. К сожалению, на ней были ошибки и из-за этого я сделал изменение - пришлось припаять проводочек, который виден на фотке платы и еще в двух местах чуть изменить разводку. Ошибка была дуракцая - выход в нижний разъем шел не с выводов q1..q7 микрух uln2004, а с CD+ и q1..q6 (ошибся на один вывод). Рисунок платы я выложил сразу правленный, но формально - я его не пробовал. У меня абсолютно такая же верхняя часть платы, а на нижней небольшие изменения.
Выявился и недостаток. По какой-то причине, через один из выводов каждой uln2004 даже в закрытом состоянии течет небольшой ток. Поэтому шесть линеек немного светятся. Красные и синие - вообще еле заметно, зеленая - сильнее. Яркость свечения на глаз примерно соответствует яркости 1 из 255.
По поводу программной части:
Мне удалось достигнуть выполнения таймера примерно за 370 тактов при работе на 42 канала. Это дает возможность обеспечить частоту мигания около 150 герц.
Если же откомпилировать проект с опцией компилятора, расчитанной на максимальное быстродействие, то получим около 315 тактов на выполнение прерывания, это дает возможность выставить частоту 180 герц. Это даже быстрее, чем нынешний вариант на 24 канала.
Ниже привожу исходник для avr atudio, который у меня работает. Если захотите взять отсюда исходники на 24 канала, потребуется из этих исходников взять:
#define TCNT2_Const 256-50
далее инициализация таймера
TCCR2B |= (1<<CS21); //установка предделителя 8
далее - основной цикл for(; можете перенести как есть в setup()
до loop() в этом случае дело не дойтет, и ладно. Там надо будет поменять две функции работы с serial (у меня uart... ).
Таймер - берете как есть и делаете изменение:
1. Изменяете цикл
for (j=6;j>0;j--)
на
for (j=3;j>0;j--)
2. Добавляете перед строками
SPDR = byte_shift; //отправляем в шину SPI
}
PWM_Counter++;
еще две строки
if(*CBright>PWM_Counter_v) byte_shift|= 1;
CBright++;
Тут дело в том, что у меня на плате на каждом шифте используется только 7 выводов из 8. Причем используются не 0..6 биты, а 1..7 (см. схему чуть выше). Поэтому я и формирую только 7 битов, а затем подвигаю эти биты на 1 влево, чтобы они попали на 1..7. Вам же в 0 бит после этого надо будет записать данные - этими двумя добавляемыми строками.
Код:
#include <iopins.h>
#include <pinlist.h>
#include <latch.h>
#include "uart.h"
using namespace IO;
#include <avr/version.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define BrightnessSize 42
#define TCNT2_Const 256-45 //256-9
#define MaxBlank 65000
uint8_t volatile PWM_Counter=0;
uint8_t Brightness[BrightnessSize];
typedef Pb2 SSPin;
typedef Pd0 SerialIn_Pin;
typedef Pd1 SerialOut_Pin;
void Timer(void);
int main (void)
{
uint8_t i;
i=1;
DDRB=0;;
Pb5::SetDirWrite();
Pb3::SetDirWrite();
Pd7::SetDirWrite();
SSPin::SetDirWrite();
SPSR = (1<<SPI2X); // SPI set clock rate fck/2
SPCR = (1<<SPE)|(1<<MSTR); // Enable SPI, Master
for (int i=0;i<BrightnessSize;i++) //обнулить массив
{
Brightness[i]=0;
}
uartInit();
uartSetBaudRate(115200);
SerialIn_Pin::SetDirRead();
SerialOut_Pin::SetDirWrite();
TIMSK2 &= ~(1<<TOIE2); //разрешения прерывания по переполнению таймера/счетчика Т2
TCCR2A &= ~((1<<WGM21) | (1<<WGM20));// Режим работы таймера/счетчика
TCCR2B &= ~(1<<WGM22);// Режим работы таймера/счетчика
TCCR2B |= (1<<CS21); //установка предделителя 64
ASSR &= ~(1<<AS2); //Выбор источника синхронизации таймера если AS2=0 от системного генератора
TCNT2 = TCNT2_Const; // 16000000/256/100/64=8 tcnt2=256-8=248.
TIMSK2 |= (1<<TOIE2);//Разрешение прерывания по переполнению Т2.
sei();
uint16_t blank=0;
uint8_t TimerCurrPWM=0;
for(;;)
{
if (!uartReceiveBufferIsEmpty())
{
uint8_t b=uartGetByte();
if (i==BrightnessSize)
{
if (b == 255)
{
i=0;
blank=0;
}
else
blank++;
}
else
{
Brightness[i]=b;
blank=0;
i++;
}
}
else
{
if (blank<MaxBlank)
{
if (TimerCurrPWM!=PWM_Counter)
{
blank++;
TimerCurrPWM=PWM_Counter;
}
}
else
{
for (uint8_t j = 0; j < BrightnessSize; j++)
Brightness[j] = 15;
blank=MaxBlank;
}
}
}
}
//void Timer(void)
SIGNAL(TIMER2_OVF_vect)
{
Pd7::Set();
Pd7::Set();
Pd7::Clear();; //формируем положительный импульс защелки
uint8_t PWM_Counter_v=PWM_Counter; // поскольку PWM_Counter объявлена как valotile, обращение к ней - всегда обращение в память. Поэтому создаем локальную копию, она будет храниться в регистре.
TCNT2 = TCNT2_Const;
uint8_t byte_shift;
uint8_t * CBright;
CBright = (uint8_t *)&Brightness[0];
uint8_t j=0;
for (j=6;j>0;j--)
{
byte_shift = 0;
if(*CBright>PWM_Counter_v) byte_shift|= 1;
CBright++;
byte_shift = byte_shift<<1;
if(*CBright>PWM_Counter_v) byte_shift|= 1;
CBright++;
byte_shift = byte_shift<<1;
if(*CBright>PWM_Counter_v) byte_shift|= 1;
CBright++;
byte_shift = byte_shift<<1;
if(*CBright>PWM_Counter_v) byte_shift|= 1;
CBright++;
byte_shift = byte_shift<<1;
if(*CBright>PWM_Counter_v) byte_shift|= 1;
CBright++;
byte_shift = byte_shift<<1;
if(*CBright>PWM_Counter_v) byte_shift|= 1;
CBright++;
byte_shift = byte_shift<<1;
if(*CBright>PWM_Counter_v) byte_shift|= 1;
CBright++;
byte_shift = byte_shift<<1;
SPDR = byte_shift; //отправляем в шину SPI
}
PWM_Counter++;
}
ps. Я с этой темой почти наигрался, дальше прыгать вроде некуда. На телек я это вешать не буду. Поэтому думаю, что через короткое время буду готов продать плату вместе с 5-ю метрами светодиодной ленты (частично порезанной на 14 20-см кусков). Для того, чтобы из нее сделать эмбилайт, вам потребуется купить шлейф 34 жилы с шагом 1.27мм (длиной в периметр телевизора) и десяток или чуть больше разъемов на него. Ну и если решите использовать не 20-см куски, то еще и надо чуть-чуть уметь паять.
Ну и, естественно, потребуется Carduino
UPD. Приложил схему и плату в Eagle.