Tuesday, August 19, 2014

შიფტ რეგისტრები და 7 სეგმენტიანი დისპლეი

seq15a.gif
ეს არის მეორე ტუტორიალი. მას შემდეგ რაც დავდე პირველი ტუტორიალი მიკრო კონტროლერის პინების მანიპულაციის შესახებ, ახლა დროა, ცოტა გავერთოთ და განვიხილოთ ისეთი ელექტრონული ელემენტი, როგორიცაა შიფტ რეგისტრი. ტუტორიალის დაწყებამდე კი დიდ მადლობას ვუხდი bildr.org-ის ადმინისტრაციას ვიზუალური ელემენტების გამოყენების ნების დართვისთვის.



რა არის Shift Register
შიფტ რეგისტრი არის ინტეგრირებული მოწყობილობა, რომელიც სერიულ სიგნალს გარდაქმნის პარალელურად. ეს ძალიან მნიშვნელოვანი ელემენტია იმ მომენტში, როდესაც მიკრო კონტროლერის პინების დაზოგვა არის საჭირო და პროექტისთვის მნიშვნელოვანი. ხშირად მას იყენებენ დიოდების (დიოდში ვგულისხმობ შუქდიოდს) სამართავად.


სანამ მაგალითზე გადავიდოდეთ, აგიხსნით, როგორ მუშაობს ეს ელექტორნული ელემენტი. ახსნა რომ მარტივი გახდეს, მაგალითისთვის ავიღებ M74HC595 შიფტ რეგისტრს, რომელიც საკმაოდ გავრცელებული და პოპულარული მიკრო სქემაა და ფართოდ გამოიყენება სხვადასხვა ელ. მოწყობილობებში. თუ მონაცემთა ფაილს განვიხილავთ, მეორე გვერდზე გვექნება ასეთი სქემატური ნახაზი და პინების სახელები


574px-SN74HC595-pinout.png


595-logic-diagram.png


სადაც მოცემულია შიფტ რეგისტრის სამი ძირითადი შემადგენელი ელემენტი, რომლებსაც წარმოადგენენ მართკუთხა ყუთები. ზედა არის თვით შიფტ რეგისტრი, რომელიც ატარებს 1 ბიტს, ერთ სიგნალზე. ეს სიგნალი კი მოდის Serial In პინიდან. როდესაც 8 ბიტი შეივსება, QH’-დან გამოდის სიგნალი. SCK პინი კი, განსაზღვრავს როდის ხდება შიფტირება, ანუ ბიტების წინ წაწევა. როდესაც გვინდა 8 ბიტის შიფტ რეგისტრიდან კოპირება Storage Register-ში, საჭიროა RCK პინის შესავამისი კონტროლი.


როდესაც 8 ბიტი შენახულია მახსოვრობის რეგისტრში, ეს ინფორმაცია გადაეცემა მესამე შემადგენელ საფეხურს, რომლის “ჩართვასაც” უზრუნველყოფს G პინზე დაბალი (ლოგიკური ნულიანი) სიგნალის მიწოდება. ასევე მონაცემთა ფაილში მოგვეპოვება ასეთი ცხრილიც, რომელიც მთლიანად აღწერს შიფტ რეგისტრის მუშაობის პრინციპს.


595-truth-table.png


უფრო გარკვევით რომ ვთქვათ ეს შიფტ რეგისტრი (595) არის 8ბიტიანი და მის ადგილას წარმოვიდგინოთ ერთმანეთზე მიწყობილი ოთახები, რომლებიც ჰორიზონტალურად არის განლაგებული. ეს ოთახები ერთმანეთთან არიან დაკავშირებული კარებებით და ბოლო ოთახს ორი კარები, აქვს: შესასვლელი მეორე ოთახიდან და გარეთ გასასვლელი. ხოლო პირველ ოთახს 2 კარები აქვს: 1 მეორე ოთახში შესასვლელი, მეორე გარედან შესასვლელი, რომ ხალხი შევიდეს (სინამდვილეში ორობითი ინფორმაცია). SCK-ის სიგნალზე (მაღალ სიგნალზე) რეგისტრში იწერება ის ინფორმაცია, რაც არის SI პინზე (ა). როდესაც SCK მიიღებს დაბალ მნიშვნელობას (ლოგიკური ნულიანი), ამ დროს არაფერი ხდება, ხოლო როდესაც SCK-ის მდგომარეობა ისევ ლოგიკურ ერთიანს გაუტოლდება, მაშინ უკვე იმავე რეგისტრში იწერება ის ინფორმაცია რაც SI პინზეა, ხოლო ძველი ჩანაწერი (ა) ინაცვლებს ერთი რეგისტრით მარჯვნივ. თუ დავუშვებთ იმას, რომ ოთახში მხოლოდ ერთი ადამიანი უნდა იყოს, მაშინ როდესაც გარედან შემოვა სხვა ადამიანი პირველ ოთახში მყოფი ადამიანი ვალდებულია გადაინაცვლოს შიგნით, ანუ ერთი ოთახით წინ. როდესაც ოთახებში ადამიანის საერთო რიცხვი აღემატება 8-ს და მეცხრე ადამიანი ცდილობს შესვლას, მაშინ ბოლო ოთახში მყოფი ადამიანი იძულებულია გავიდეს გარეთ, ასე ემართება ინფორმაციასაც, მხოლოდ იმ განსხვავებით, რომ ის კარებით არ გამოდის გარეთ, არამედ Serial Out პინით. ეს ყველაფერი კი კარგად არის გამოსახული ქვემოთ მოცემულ სურათზე.


shiftRegister.png


ახლა დროა ვნახოთ კოდი, რომლითაც მარტივად შეგვძლია შიფტ რეგისტრთან კომუნიკაციის დამყარება და მისი მართვა. პირველ რიგში კი ქვემოთ მოცემული სურათი გვიჩვენებს რომელიც პინი, რომელ პინს უკავშირდება (მიკრო კონტროლერიდან 595 ჩიპს).


74HC959_right_2.png
ეს მაგალითი გვიჩვენებს LED-ის კონტროლს შიფტ რეგისტრის გამოყენებით, ხოლო კოდი კი შემდეგია.


boolean _buffer[8]; //გლობალური ცვლადი
int8_t DataCap = 8; //გლობალური ცვლადი, აღწერს რამდენი ერთეული ინფორმაციის
// დატევა შეიძლება რეგისტრში
...
void WriteReg()
{
PORTB = (0<<RCLK);


for (int8_t i = 0; i < DataCap; i++)
{
PORTB = (0<<SRCLK);
PORTB = (_buffer[i]<<SER);
PORTB = (1<<SRCLK);
}


PORTB = (1<<RCLK);
}
...
void RegPinInit()
{
int8_t SER = 0;
int8_t RCLK = 1;
int8_t SRCLK = 2;


DDRB = (1<<SER) | (1<<RCLK) | (1<<SRCLK);
}


აქვე მინდა ერთი რამ აღვნიშნო - რატომ ხდება for ციკლში დეკრემენტაცია და რატომ არა ჩვეულებრივ ინკრემენტაცია. ვნახოთ ქვემოთ მოცემული სურათი, რომელზეც მწვანე ფერით აღნიშნული რიცხვები გვიჩვენებს ინფრომაციის ნომერს და არა მათ მნიშვნელობას, რადგან მნიშვნელობები არ შეიძლება იყოს სხვა რამ ერთიანის და ნულიანის გარდა.


shiftRegister.png
_buffer მასივში თუ არის ჩაწერილი შემდეგი ბაიტი 0b10110110 და გვინდა ამ ინფორმაციის რეგისტრში ჩაწერა ვიყენებთ დეკრემენტულ for ციკლს, რადგან მენულე ადგილას უნდა მოხვდეს მენულე ელემენტი (მოცემული ბაიტის 0b10110110), რომელიც _buffer მასივის მეშვიდე ელემენტია (თვლა იწყება ნულიდან და სწორედ ამიტომ მეშვიდე და არა მერვე). შემდეგ უნდა ჩავწეროთ მეექვსე ელემენტი და ასე შემდეგ. საბოლოოდ კი მივიღებთ შემდეგს


shiftRegister (1).png


რეგისტრის “გაწმენდა შესაძლებელია შემდეგნაირად. ჯერ გავანულოთ _buffer მასივში არსებული ელემენტები და შემდეგ ჩავწეროთ. ეს თავიდან აგვაცილებს დამატებითი კავშირის შექმნას ჩიპთან.


არსებობს სიტუაციები, როდესაც საჭიროა ერთის ნაცვლად ორი, სამი ან ხუთი რეგისტრის გამოყენება. ამ დროს საჭიროა, რეგისტრების ერთმანეთთან “გადაბმა”. თუ ზემოთ მოცემულ სურათს დააკვირდებით (სადაც რეგისტრი არდუინოზეა მიერთებული), შეამჩნევთ ყვითელ ხაზს. ეს არის Serial In ხაზი, საიდანაც მოედინება ინფორმაცია. ხოლო რეგისტრის შევსების შემთხვევაში ინფორმაცია გამოდის QH’ პინიდან. რამდენიმე რეგისტრის გამოყენებისას პირველი რეგისტრის QH’ პინი უკავშირდება მეორე რეგისტრის Serial In პინს, საიდანაც ხდება ინფორმაციის გადინება. ქვემოთ მოცემული სქემატური ნახაზი კი გამოიყენება ორი რეგისტრის შემთხვევაში


74HC595_4.png
სადაც QH’ პინი შეერთებულია SI პინზე. ამ შემთხვევაში ასევე მოგვიწევს პროგრამის მოდიფიკაციაც, რადგან ახლა 8 ბიტის ნაცვლად გვაქვს 16 ბიტი და რამდენ რეგისტრსაც გამოვიყენებთ იმდენჯერ 8 ბიტი (ორის ნაცვლად ენ რაოდენობის რეგისტრის გამოყენების შემთხვევაში). პროგრამაში (ორი რეგისტრისთვის) შევა შემდეგი ცვლილებები


boolean _buffer[16]; //გლობალური ცვლადი
int8_t DataCap = 16; //გლობალური ცვლადი, აღწერს რამდენი ერთეული ინფორმაციის
// დატევა შეიძლება რეგისტრში
//ან შეიძლება ისეთი კონტსტრუქციის მოფიქრება, სადაც მხოლო რეგისტრის ოდენობის
//შეცვლა გახდება საჭირო, კერძოდ
// #define RegNum 2
// boolean _buffer [ (RegNum * 8) ];
// int8_t DataCap = RegNum * 8;
//ახლა მხოლოდ RegNum ცვლადის შეცვლით შეიცვლება ყველა ინფორმაცია შესავამისად
...
void WriteReg()
{
PORTB = (0<<RCLK);


for (int8_t i = 0; i < DataCap; i++)
{
PORTB = (0<<SRCLK);
PORTB = (_buffer[i]<<SER);
PORTB = (1<<SRCLK);
}


PORTB = (1<<RCLK);
}
...
void RegPinInit()
{
int8_t SER = 0;
int8_t RCLK = 1;
int8_t SRCLK = 2;


DDRB = (1<<SER) | (1<<RCLK) | (1<<SRCLK);
}


ამდენი ინფორმაციის შემდეგ უკვე შეგვიძლია გადავიდეთ მაგალითის განხილვაზე, სადაც გამოვიყენებ 7 სეგმენტიან დიოდს (ჩემს შემთხვევაში ეს არის რუსული დიოდი - ALC338A1). ჩვენი პრობლემა კი შემდგომში მდგომარეობს:


პრობლემა - ხშირად გვინახავს შუქნიშნები, რომლებსაც აქვთ ციფერბლატი და გამოიყენება დროის ვიზუალურად გამოსახვისთვის, რაც ძალიან მოხერხებულია მომხმარებლისთვის. ჩვენი მთავარი მიზანია, რომ გავაკეთოთ სწორედ ასეთი ციფერბლატი, რომელიც იქნება 2 პოზიციანი. მოგვიანებით კი შემოგთავაზებთ ჩემს მიერ გაკეთებულ საათს, რომელიც დაფუძნებული იქნება ამ პროექტსა და მომავალ ტუტორიალზე.


პრობლემის გადასაწყვეტად საჭიროა გავარჩიოთ 7 სეგმენტიანი დისპლეი, რომელიც ინდივიდუალური LED-ებისგან შედგება და მათ გასაკონტროლებლად საჭიროა შიფტ რეგისტრის გამოყენება, რადგან ეს პინებს დაგვიზოგავს მიკრო კონტროლერზე. ქვემოთ მოცემულია პინების შეერთების გეგმა, რომლითაც ვისარგებლებ ჩემს დემო ვერსიაში.
ShiftReg_Tutorial.png
ჩვენი კოდისთვის დაგვჭირდება ის, რომ მიკრო კონტროლერს “ვასწავლოთ” თვლა, მხოლოდ ცხრამდე. ამის შემდეგ კი მხოლოდ მარტივი პროგრამული ტექნიკით შეგვიძლია 100-მდე დავითვალოთ. პროგრამაში არის ფუნქცია SetNum, რომელიც იღებს ორ არგუმენტს: num და RegID. პირველი პარამეტრი რეგისტრში იმ ბიტებში წერს ერთიანებს, რომლებიც შესაბამის ციფრს გამოსახავს. თუ num = 9, მაშინ რეგისტრში ჩაიწერება 9-ის შესაბამის პინებში ერთიანი, და აინთება ეს ციფრი დისფლეიზე. მეორე მხრივ კი RegID პარამეტრი უზრუნველყოფს რომელ რეგისტრს მიმართოს პროგრამამ, პირველს (ათეულების რეგისტრს) თუ მეორეს (ერთეულების რეგისტრს). ასევე არის მეორე ფუნქციაც, რომელშიც ინტეგრირებულია როგორც ციფრის, ისე რიცხვის გამოსახვა, რომლის სახელია SetNum_TPS, რომლის პარამეტრიც არის num, და ეს პარამეტრი შეიძლება იცვლებოდეს 0-დან 99-მდე. ასევე კოდში არის საათის ფუნქციაც, რომელიც რეალურ საათს წარმოადგენს და ეყრდნობა AtMega328P-PU-ს ტაიმერს, რომელზეც მოგვიანებით გავაკეთებ ტუტორიალს. პროგრამის კოდი შეგიძლიათ იხილოთ ქვემოთ. ასევე გთავაზობთ სურათებსა და ვიდეოს.

IMG_20140814_215846.jpg
IMG_20140815_001812.jpg





ეს კი არის კოდის ლინკი, რომელიც განთავსებულია ჩემს დრაივზე. ყოველი ცვლილებები ავტომატურად აისახება ამ ფაილზე და შეგეძლებათ გადმოწეროთ პირადაპირ განახლებული და მუშა ვერსიები. ქვემოთ კი მოცემულია ვიდეო, რომელზეც არის საათის ფუნქციის დემო, სადაც 9:40-დან საათის საცდელი ვერსია ინაცვლებს 9:41-მდე.








No comments:

Post a Comment