این پروژه نحوه دریافت 12 خروجی PWM آنالوگ از ATtiny85 را شرح می دهد، بنابراین می توانید 12 LED را به طور جداگانه کنترل کنید:

هر LED را می توان روی روشنایی بین 0 (خاموش) تا 63 (به طور کامل روشن) تنظیم کرد. برنامه نمایشی یک موج چرخشی از نور را نشان می دهد که در اطراف دایره حرکت می کند.
این مدار می تواند برای طیف وسیعی از کاربردها، از نمایشگرهای LED ، جواهرات، یا کنترل چراغ های خانه عروسک استفاده شود. یک خط ورودی/خروجی آزاد در ATtiny85 باقی میگذارد، بنابراین میتوانید نمایشگر نور را از طریق یک سنسور یا ورودی سریال کنترل کنید.
مقدمه
روش معمول دریافت خروجی PWM در تراشه های ATtiny و ATmega استفاده از تایمرهای سخت افزاری داخلی برای تولید یک خروجی شکل موج با نسبت علامت به فضای مناسب است. دستور آردوینو analogWrite() به این صورت است . ATtiny85 و ATtiny84 فقط دو تایمر دارند که هر کدام دو خروجی دارند، بنابراین حداکثر تعداد خروجی PWM که می توانید از این طریق از هر یک از این تراشه ها دریافت کنید، چهار تایمر است.
یک راه جایگزین برای دریافت خروجی PWM استفاده از یکی از تایمرها برای ایجاد وقفه است. سپس، در روال سرویس وقفه، هر یک از خروجی ها را متناسب با نسبت های علامت-فضای مورد نظر برای خروجی تغییر می دهید. این به برنامه نویسی کمی بیشتری نیاز دارد، اما به شما امکان می دهد در هر خروجی PWM داشته باشید. در مورد ATtiny85 این می تواند 5 خروجی PWM و در ATtiny84 یازده خروجی PWM به شما بدهد.
این پروژه یک قدم جلوتر می رود. از charlieplexing استفاده می کند تا به شما امکان می دهد یک LED را بین هر جفت خط ورودی/خروجی وصل کنید و سپس به شما امکان می دهد روشنایی هر LED را با PWM کنترل کنید.
شارلی پلکسینگ
Charlieplexing از این واقعیت استفاده می کند که هر خط ورودی/خروجی می تواند یکی از سه حالت را داشته باشد: کم، زیاد یا امپدانس بالا وقتی به عنوان ورودی تعریف شود. همه خطوط ورودی/خروجی به عنوان ورودی تعریف میشوند که هیچ LED روشن نباشد، و برای روشن کردن یک LED، یک خط ورودی/خروجی را پایین و یک خط ورودی/خروجی را بالا میبرید. سپس LED در تقاطع آن دو خط روشن می شود.
سادهترین راه برای تجسم این موضوع، رسم نمودار است، که در آن هر سلول ترکیبی از یک خط ورودی/خروجی پایین و یک خط ورودی/خروجی دیگر را نشان میدهد. شما می توانید یک LED در هر سلول قرار دهید. مربع های خاکستری موقعیت هایی را نشان می دهند که نمی توانید LED را وصل کنید، زیرا یک خط I/O نمی تواند همزمان بالا و پایین باشد:

بنابراین تعداد کل LED هایی که می توانید با چهار خط ورودی/خروجی کنترل کنید 12 عدد است.
مدار
مدار اینجاست:

همانطور که در بالا نشان داده شده است، هر یک از 12 LED به دو خروجی ATtiny85 متصل می شود. من اتصالات را ترسیم نکرده ام زیرا بدون آن واضح تر است. PB4 استفاده نشده است، بنابراین می توان آن را به یک سنسور یا مدار خارجی متصل کرد.
کل مدار به خوبی روی یک بربورد قرار می گیرد .
برنامه
آرایه Level[] برای تعیین سطح هر LED استفاده می شود. هر مقدار می تواند بین 0 تا 63 باشد. Level[0] روشنایی LED 0 را در نمودار مدار تنظیم می کند و غیره. به عنوان مثال، برای ارائه نمایشگر نشان داده شده در ابتدای این مقاله، از:
uint8_t Level[12] = {3, 63, 3, 63, 3, 63, 3, 63, 3, 63, 3, 63 };
این برنامه از یک روال سرویس وقفه برای تولید خروجی PWM در هر LED استفاده می کند. این توسط Timer/Counter1 فراخوانی می شود که در setup() تنظیم شده است
تا یک وقفه تطبیق مقایسه در 16 کیلوهرتز ایجاد کند. بنابراین، هر LED با سرعت 16000/64/4 یا 62.5 هرتز بهروزرسانی میشود، به اندازه کافی سریع برای جلوگیری از هرگونه سوسو زدن قابل مشاهده:
void setup() {
// Set up Timer/Counter1 to multiplex the LEDs
TCCR1 = 1<<CTC1 | 2<<CS10; // Divide by 2
GTCCR = 0; // No PWM
OCR1A = 0;
OCR1C = 250-1; // 16kHz
TIMSK = TIMSK | 1<<OCIE1A; // Compare Match A interrupt
}
با این کار Timer/Counter0 برای استفاده توسط توابع ()delay و millis () آردوینو آزاد است .
سپس روال سرویس وقفه تمام کارها را انجام می دهد:
ISR(TIM1_COMPA_vect) {
static uint8_t first, ramp, column, bits, colbit;
ramp = (ramp+1) & 0x3F; // Count from 0 to 63
if (ramp == 0) {
bits = 0x07; // All on
column = (column + 1) & 0x03;
first = column * 3; // First LED in this column
colbit = 1<<column;
}
if (Level[first] == ramp) bits = bits & 0x06;
if (Level[first+1] == ramp) bits = bits & 0x05;
if (Level[first+2] == ramp) bits = bits & 0x03;
uint8_t mask = colbit - 1;
uint8_t outputs = (bits & mask) | (bits & ~mask)<<1;
DDRB = (DDRB & 0xF0) | outputs | colbit;
PORTB = (PORTB & 0xF0) | outputs;
}
این زمان را با استفاده از ستون شمارنده به چهار برش تقسیم می کند و در طول هر برش خروجی مربوط به آن ستون با استفاده از colbit کم می شود .
هر برش با استفاده از رمپ پیشخوان به 64 زیربخش تقسیم می شود . هنگامی که سطح شیب دار صفر است، سه LED در ستون فعلی روشن می شوند. از آنجایی که سطح شیب دار تا 63 شمارش می کند، سطح تخصیص داده شده به هر LED مشخص می کند که در چه نقطه ای خاموش می شود: سطح 0 بلافاصله آن را خاموش می کند، در حالی که سطح 63 آن را برای 63/64 برش زمانی روشن می گذارد.
بیت های متغیر مشخص می کند که کدام یک از سه LED در ستون فعلی باید در فراخوانی جاری روال وقفه روشن باشد. سپس در چهار خط I/O در خروجی های متغیر تقسیم می شود و در پورت خروجی نوشته می شود.
روال سرویس وقفه حدود 15 میلی ثانیه طول می کشد تا با ساعت 8 مگاهرتز اجرا شود، که تعداد سطوح آنالوگ ما را محدود می کند. من در 64 سطح قرار گرفتم، که زمان کافی برای پردازشگر برای انجام سایر عملکردها بین وقفه ها باقی می گذارد.
در نمونه اولیه به دلیل محدودیت های تخته نان، من نتوانستم LED ها را به ترتیب عددی در اطراف دایره مرتب کنم:

این یک مشکل نیست. من فقط از یک آرایه دوم، Order[] برای تغییر مسیر هر عنصر در آرایه Level[] استفاده کردم . اگر LED ها را به ترتیب ترتیب دهید، می توانید آرایه Order[] را حذف کنید .
نسخه ی نمایشی
برای نشان دادن مدار، برنامه LED ها را روی یک محدوده روشنایی پیوسته تنظیم می کند:
uint8_t Level[12] = {1, 1, 3, 7, 15, 31, 63, 31, 15, 7, 3, 1 };
سپس برنامه اصلی در
حلقه() ، سطوح را در یک موقعیت در هر دهم ثانیه جابجا می کند تا ظاهر موجی را در اطراف دایره ایجاد کند:
void loop () {
// Wave demo
uint8_t temp = Level[Order[11]];
for (int i=11; i>0; i--) Level[Order[i]] = Level[Order[i-1]];
Level[Order[0]] = temp;
delay(100);
}
جریان مدار
برگه داده ATtiny85 توصیه می کند که جریان کل درایو در تمام پورت های ورودی/خروجی نباید از 60 میلی آمپر تجاوز کند. یک LED معمولاً 1.8 ولت افت می کند، بنابراین با یک 5 ولت جریان از طریق هر LED در هنگام روشن بودن کامل توسط مقاومت 220Ω به (5 – 1.8)/220 یا 14.5 میلی آمپر محدود می شود. با این حال، به دلیل مالتی پلکس شدن، هر LED فقط برای 1/4 زمان روشن است، بنابراین جریان کل با تمام 12 LED در حداکثر روشنایی 43.5 میلی آمپر خواهد بود.