вот, выкладываю сурсы.
Соединение такое:
PortD Pin7 (Arduino digital 7) соединен с RCK 74hc595 (Pin 12)
PortB Pin3 (Arduino digital 11) соединен с SI 74hc595 (Pin 14)
PortB Pin5 (Arduino digital 13) соединен с SCK 74hc595 (pin 11)
Код выполнен в AVR Studio, поэтому в нем нет
void Setup(void)
и
void loop(void)
вместо них единый
void main(void)
Brightness[48] - массив яркостей, который нужно показать.
Для примера яркость меняется по циклическому закону - увеличивается.
for (i=0;;i++)
{
_delay_ms(1);
for (uint8_t j=0;j<48;j++)
Brightness[j]++;
}
bitdata[6] (48 бит) побитово хранит то, что нужно выдать в 595-ые. Параллельно подключенные светодиоды одного цвета одной зоны соответствуют одному биту.
Когда начинается новая итерация ШИМ (PWM_Counter==0) bitdata заполняется битовыми единицами - 0xFF.
if (PWM_Counter==0)
{
// по умолчанию при PWM_Counter==0 все светодиоды горят, а затем гаснут по мере необходимости.
for (uint8_t l=0;l<6;l++)
bitdata[l]=0xff;
Затем, каждый элемент Brightness[] проверяется и если он меньше или равен PWM_Counter, то соответствующий бит в bitdata[] сбрасывается и этим гасится одна зона одного цвета.
Таким образом, из 256 итераций цикла (PWM_Counter от 0 до 255) каждый из 48 битов в bitdata[] установлен в 1 ровно столько итераций, сколько установлено в байтах яркости Brightness[].
Код тут оптимизирован, но несмотря на это код из таймера жрет порядка 70% процессорного времени. _delay_ms(1) реально длится 2.5 мс.
Если же повторяющийся кусок убрать в цикл, то быстродействие падает еще почти в полтора раза - _delay_ms(1) ждет уже 3.5 мс...
Замеры производились при оптимизации -O3.
Если же проводить замеры со стандартной для Arduino оптимизации -O2
то приведенный код дает
3.5 мс вместо _delay_ms(1)
а в случае замены повторяющегося куска циклом, таймер начинает потреблять 100% процессорного времени.
PHP код:
#define TCNT2_Const 256-9
uint8_t volatile PWM_Counter_v;
uint8_t Brightness[48];
uint8_t bitdata[6];
void SPI_MasterInit(void)
{
/* Set MOSI and SCK output, all others input */
DDRB |= (1<<3)|(1<<5);
/* Enable SPI, Master, set clock rate fck/2 */
SPSR =(1<<SPI2X);
SPCR = (1<<SPE)|(1<<MSTR);
}
void SPI_MasterTransmit(char cData)
{
uint8_t a=SPSR;
a=SPDR; // очищаем SPIF перед отправкой данных, чтобы этот бит выставился только после завершения отправки
SPDR = cData;
}
void Out(void)
{
while(!(SPSR & (1<<SPIF)));// ждем пока не завершится передача данных
uint8_t a=SPDR;
PORTD |= (1<<PIN7);
PORTD &= ~(1<<PIN7);
PORTD = PORTD ^ (1<<PIN3);
}
SIGNAL(TIMER2_OVF_vect)
{
TCNT2 = TCNT2_Const;
Out();
uint8_t PWM_Counter=PWM_Counter_v;
{
if (PWM_Counter==0)
{
// по умолчанию при PWM_Counter==0 все светодиоды горят, а затем гаснут по мере необходимости.
for (uint8_t l=0;l<6;l++)
bitdata[l]=0xff;
}
uint8_t * Bright;
Bright= &Brightness[0];
for (uint8_t j=0;j<6;j++)
{
uint8_t CurrData=bitdata[j];
uint8_t bit=1;
// for (uint8_t k=0;k<8;k++)
// 0
{
if (*Bright<=PWM_Counter)
CurrData &= ~bit;
bit=bit<<1;
Bright++;
}
// 1
{
if (*Bright<=PWM_Counter)
CurrData &= ~bit;
bit=bit<<1;
Bright++;
}
// 2
{
if (*Bright<=PWM_Counter)
CurrData &= ~bit;
bit=bit<<1;
Bright++;
}
// 3
{
if (*Bright<=PWM_Counter)
CurrData &= ~bit;
bit=bit<<1;
Bright++;
}
// 4
{
if (*Bright<=PWM_Counter)
CurrData &= ~bit;
bit=bit<<1;
Bright++;
}
// 5
{
if (*Bright<=PWM_Counter)
CurrData &= ~bit;
bit=bit<<1;
Bright++;
}
// 6
{
if (*Bright<=PWM_Counter)
CurrData &= ~bit;
bit=bit<<1;
Bright++;
}
// 7
{
if (*Bright<=PWM_Counter)
CurrData &= ~bit;
bit=bit<<1;
Bright++;
}
bitdata[j]=CurrData;
SPI_MasterTransmit(CurrData); // выводим очередные 8 бит
}
}
PWM_Counter_v++;
}
int main(void)
{
PWM_Counter_v=255;// первым делом копируем данные в CurrBrihgtness
uint8_t i;
for (i=0;i<48;i++)
{
Brightness[i]=(1<<(i&7));
// Brightness[i]=0;
}
PORTB = 0;
PORTD = 0;
DDRD |= (1<<PIN3);
DDRD |= (1<<PIN7);
SPI_MasterInit();
TIMSK2 &= ~(1<<TOIE2); //разрешения прерывания по переполнению таймера/счетчика Т2
TCCR2A &= ~((1<<WGM21) | (1<<WGM20));// Режим работы таймера/счетчика
TCCR2B &= ~(1<<WGM22);// Режим работы таймера/счетчика
TCCR2B |= (1<<CS22);
ASSR &= ~(1<<AS2); //Выбор источника синхронизации таймера если AS2=0 от системного генератора
TCNT2 = TCNT2_Const; // 16000000/256/100/64=8 tcnt2=256-8=248.
TIMSK2 |= (1<<TOIE2);//Разрешение прерывания по переполнению Т2.
sei();
for (;;)
{
_delay_ms(1);
for (uint8_t j=0;j<48;j++)
Brightness[j]++;
}
}
UPD: код я у себя тут еще оптимизировал, но выигрыш копеечный.