Jan 31, 2014

I2C კომუნიკაციის პროტოკოლი

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

ტექნოლოგიების განვითარებასთან ერთად 1982 წლიდან 2012 წლამდე კომუნიკაციის სიჩქარე 100 კილო ჰერციდან გაიზარდა 5 მეგა ჰერცამდე (შედარებისთვის 5000 კილო ჰერცი). საბოლოო ვერსია არის 5, რომელშიც გამოსწორებული წინა ვერსიის შეცდომები.
მთავარი აზრი ის არის, რომ I2C იყენებს ორ უნივერსალურ (ორმაგი მიმართულების) ხაზს. Serial Data და Serial Clock, შესაბამისად - SDA და SCL ხაზს, რომელიც აწეულია წინაღობებით. გააჩნია 7 / 10 ბიტიანი მისამართის სივრცე, რომელიც გამოყენებულ მოწყობილობებზეა დამოკიდებული. სტანდარტული ინფორმაციის გაცვლის მდგომარეობა არის 100kbit/s.

კომუნიკაციის მხრივ არსებობს 4 გარემოება, რომელიც შესაძლებელია წარმოიშვას:

1.      მაკონტროლებელი (master) კვანძი უგზავნის მონაცემს დაქვემდებარებულს (slave)
2.      მაკონტროლებელი იღებს ინფორმაციას დაქვემდებარებულიაგან
3.      დაქვემდებარებული კვანძი უგზავნის მაკონტროლებელს ინფორმაციას
4.   დაქვემდებარებული კვანძი იღებს ინფორმაციას მაკონტროლებლისგან


 
როგორც აღვნიშნე I2C კომუნიკაცია საკმაოდ მარტივია და ძალუძს რამდენიმე მოწყობილობას შორის გაცვალოს ინფორმაცია მხოლოდ 2 ინფორმაციის მიმოცვლის ხაზის გამოყენებით. ყველაზე მარტივ ფორმაში არსებობს მაკონტროლებელი მოწყობილობა, რომელსაც შეუძლია აკონტროლოს დაქვემდებარებული რამდენიმე მოწყობილობა. აღსანიშნავია ის ფაქტი, რომ SCL, ანუ რომ იმპულსების ხაზს აკონტროლებს მაკონტროლებელი მოწყობილობა. კომუნიკაციის დასამყარებლად მაკონტროლებელი მოწყობილობა აგზავნის 1 ბაიტ ინფორმაციას, რომელშიც შერეულია დაქვემდებარებული მოწყობილობის მისამართი (ID) და ჩაწერა/წაკითხვის 1 ბიტიანი მანიშნებელი. თუ განსაზღვრულია ჩაწერის ოპერაცია, მაშინ მაკონტროლებელი მოწყობილობა დაიწყებს ინფორმაციის გაგზავნას იმავე მისამართზე. ხოლო თუ 1 ბიტი მიუთითებს იმაზე, რომ წაკითხვა უნდა შესრულდეს, მაშინ დაქვემდებარებული მოწყობილობა გაგზავნის ინფორმაციას, რომელსაც წაიკითხავს მთავარი მოწყობილობა (მაკონტროლებელი). ტრანზაქციების გასაკონტროლებლად მთავარი და დაქვემდებარებული მოწყობილობები აკონტროლებენ SCL და SDA ხაზებს, რომელმაც შეიძლება შემდეგი მნიშვნელობები მიიღოს: START, STOP, ACK, NAK. ბოლო ორი შესაბამისად acknowledged და no acknowledge. მდგომარეობის დეტალები უკვე იმართება სპეციალური დრაივერების დახმარებით.



R/W - წაკითხვა/ჩაწერა, Slave Address - დაქვემდებარებულის მისამართი.

ელექტრონულად გამართულობის მხრივ, მოთხოვნები საკმაოდ მარტივია. მთავარმა და დაქვემდებარებულმა უნდა გამოიყენონ ერთი და იმავე დონის ძაბვა (შენიშვნა 1). Pull-up წინაღობების მნიშვნელობა დაახლოებით გამოითვლება კავშირის სრულ ტევადობაზე დაყრდნობით, მაგრამ 1.8K დან 10K მდე, პრაქტიკულად,  შესაძლებელია ნებისმიერი წინაღობის გამოყენება (შენიშვნა 2).

სხვადასხვა მიკრო პროცესორი ამ კომუნიკაციის იმპლემენტაციას სხვადასხვაგვარად ახორციელებს. ზოგიერთმა შეიძლება გამოიყენოს USI (Universal Serial Interface), რომელიც ასევე გამოდგება SPI კომუნიკაციებისათვის, ხოლო ზოგიერთს გააჩნია სპეციალურად ჩაშენებული TWI (Two Wire Interface) კერძოდ I2C კომუნიკაციებისათვის.

როგორც აღვნიშნე I2C მოწყობილობების მისამართი არის 7 ბიტიანი +1 ბიტი ჩაწერა/წაკითხვა. ამიტომ აქედან გამომდინარე 127 მოწყობილობის დაკავშირებაა შესაძლებელი მხოლოდ ორი ხაზის გამოყენებით. როგორც ზემოთ არის ნაჩვენები (მისამართის სურათი) 7 ბიტი წანაცვლებულია 1 ბიტით და ბოლო პირველი ბიტი გამოყენებულია, როგორც ჩაწერა/წაკითხვის მანიშნებელი.  ზოგჯერ არის ისეთი შემთხვევები, როდესაც საჭირო ხდება დაქვემდებარებული მოწყობილობის მისამართის განსაზღვრა ფიზიკურად - დაფაზე. ხშირად ბოლოდან 4 ბიტი განსაზღვრულია წინასწარ და მომხმარებელს უწევს 3 ბიტის ფიზიკურად კონფიგურაცია. მსგავსი რამ შეიძლება შეგხვდეთ გარე მახსოვრობის მიკრო სქემების სარგებლობისას.

არსებობს ასევე ისეთი გამონაკლისი შემთხვევები, როდესაც 1 მოწყობილობა იყენებს რამდენიმე მისამართს. ასეთი სიტუაცია შეიძლება შეგექმნათ EEPROM ებთან მუშაობისას, რომლებიც გარე მახსოვრობას წარმოადგენენ. დავუშვათ, რომ ავიღოთ, 24C16 ჩიპი, რომლის მახსოვრობა 16K ბაიტია, ამ დროს მონაცემთა ოდენობის გაზრდის პარალელურად (როგორც აღვნიშნეთ მისამართის ბოლოდან პირველი 4 ბიტი განსაზღვრულია, კერძოდ - 1010) აღარ არის საჭირო 2-4 (ჩათვლით) ბიტების ფიზიკური კონფიგურაცია, რადგან აზრი არ აქვს. ამიტომ მთელი მოწყობილობა იყენებს 50-57 მისამართს, რომ მთავარმა მოწყობილობამ შეძლოს მთელი 16K ბაიტი ინფორმაციის აღქმა და შესაბამისად მისი მანიპულირება მოახდინოს.

1.      ზოგჯერ დაქვემდებარებულ მოწყობილობას აქვს შეზღუდვა ძაბვაზე, მაგალითად მუშაობს მხოლოდ 2.5 – 3.3 ვოლტამდე. ეს შეზღუდვა უნდა შესრულდეს, რომ არ გაფუჭდეს მოწყობილობა. ამასთან ერთად უნდა გაითვალისწინოთ ისიც, რომ მთავარმა მოწყობილობამაც უნდა იმუშაოს ამ დიაპაზონში.
2.      სრული ტევადობის პრობლემა მანამდე არ იჩენს თავს, სანამ არ იქნება გრძელი ხაზები და დაქვემდებარებული მოწყობილობების გრძელი ჩამონათვალი.

პრაქტიკული გამოყენება

ამდენი თეორიის შემდეგ საინტერესო იქნება თუ როგორ შეგვიძლია ეს ყველაფერი გადავიტანოთ რეალურ ცხოვრებაში და ვნახოთ ამ კომუნიკაციის საშუალების რეალური შედეგები. მაგალითში გამოვიყენებ დღესდღეობით საკმაოდ პოპულარულ დაფას - Arduino  და მის IDE ს. აქსელერომეტრის ვარიანტში კი გამოვიყენებ უბრალოდ Atmega32 ჩიპს, რომელსაც USB კომუნიკაციის მხარდაჭერა აქვს.

მე პირადად კომუნიკაციის ეს ტიპი გამომიყენებია გარე მახსოვრობებისათვის, ტემპერატურისა და ტენიანობის გაზომვისათვის და აქსელერომეტრის მონაცემების წასაკითხად. აქსელერომეტრთან და გარე მახსოვრობასთან მუშაობა ერთის მხრივ უფრო რთული იმ მხრივ არის, რომ ორმხრივი კომუნიკაციაა საჭირო, მონაცემების ჩასაწერად და შემდეგ წასაკითხად (სასურველ ფორმატში აქსელერომეტრის შემთხვევაში). პირველი მაგალითი დავიწყოთ გარე მახსოვრობასთან კომუნიკაციით, რომელიც ამ მაგალითის შემთხვევაში არის 24LC256 (256K ბიტიანი = 32K ბაიტი გარე მახსოვრობა).



A1 A2 და A3 პინების მდგომარეობის მიხედვით შესაძლებელია მისამართის ამ მოწყობილობის მისამართის განსაზღვრა. მაგალითისთვის ავიღოთ, თუ სამივე ფეხს მივაერთებთ ნულთან, მაშინ მოწყობილობის მისამართი გახდება 0x50, ხოლო თუ ყველა ფეხს + ზე მივაერთებთ მისამართი შეიცვლება 0x57 ით. ყველა კომბინაციისას მისამართი იცვლება, რომლის მნიშვნელობა შესაძლებელია მონაცემთა ფურცელის ან google ის დახმარებით გაიგოთ. სიმარტივისთვის ყველა ფეხს შევაერთებ ნულთან.

Arduono სთვის კოდი კი ქვემოთ იქნება მოცემული. პირველ რიგში setup ში საჭიროა ინტერფეისის დაწყება, მხოლოდ იმის შემდეგ, რაც გლობალურ ბლოკში შემოვიტანთ სპეციალურად ამ ინტერფეისისთვის დაწერილ ბიბლიოთეკას - Wire.h. საბოლოოდ კოდი გამოდის შემდეგნაირი, რომელსაც დაწვრილებით გავარჩევ (კოდი წარმოდგენილია, როგორც გრაფიკული ფაილი, მისი რედაქტირება შესაძლებელია სტატიის ბოლოს არსებული ფაილის საშუალებით Arduino IDE ს გამოყენებით).



თავიდან შეიძლება writeEEPROM და readEEPROM ფუნქციები ძნელი აღსაქმელი გახდეს, მაგრამ ახსნილი ვფიქრობ გაცილებით მარტივი იქნება. კოდში მაქვს სტანდარტული loop და setup ფუნქციები (მეთოდი - C# ში). ამჯერად მეორე ფუნქცია ცარიელია, რადგან მანიპულირება სურვილისებრ შეგიძლიათ მოახდინოთ. პირველად setup ფუნქციაში ვახდენ Serial პორტის ინიციალიზებას, რომლის პარამეტრი კომუნიკაციის სიჩქარეა (სერიული პორტი კომპიუტერთან კომუნიკაციის საშუალებაა Arduino სთვის). სერიული მონიტორისა და პროცესორის კომუნიკაციის სიჩქარე უნდა დაემთხვეს, რომ კომუნიკაცია წარმატებით შედგეს, მონაცემების გამოსატანად.

ამის შემდეგ ხდება I2C კომუნიკაციის დაწყება, რაც (სტანდარტულად) მიკროპროცესორის ანალოგიურ მეხუთე და მეოთხე პინს გამოიყენებს კომუნიკაციისთვის. ამის შემდეგ იქმნება ცვლადი, მისმართი, რომელიც მიუთითებს ადგილს მახსოვრობაში ინფორმაციის ჩაწერის მისამართს. ყოველ ჯერზე უნდა გაიგზავნოს 1 ბაიტი. როგორც აღვნიშნეთ ჩემს შემთხვევაში მოწყობილობის მოცულობაა 32K ბაიტი. თუ მისამართს მივუთითებ ნულს, მაშინ ინფორმაციის ჩაწერა დაიწყება ნულიდან, ხოლო თუ 321, 321 დან. ასევე მაქვს ორი ძირითადი ფუნქცია writeEEPROM და readEEPROM, რომლებიც მთელ შავ სამუშაოს ასრულებენ.

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

ინფორმაციისთვის : Arduino IDE ში int ცვლადი იკავებს 2 ბაიტს, ხოლო char ცვლადი 1 ბაიტს. I2C კომუნიკაციის გამოყენებისას შეზღუდვა ის არის, რომ ყოველი გაგზავნილი პაკეტი უნდა იყოს 1 ბაიტის ტოლი.

პირველ რიგში, ჩაწერის ფუნქცია იწყება Wire.beginTransmission() ით, რაც მოწყობილობას ამცნობს იმას, რომ კომუნიკაციის დამყარება გვსურს მასთან. შემდეგ ვგზავნით მეხსიერების იმ მისამართს, სადაც ჩაწერის განხორციელება გვსურს. რადგან გარე მეხსიერებაში 32000 სხვადასხვა მისამართი არსებობს, რისთვისაც 16 ბიტიანი ცვლადის გამოყენებაა საჭირო, უნდა მოვახერხო და ეს 16 ბიტი, ორ 8 ბიტიან ცვლადად დავშალო და მისამართი ისე გავაგზავნო. მეორე ხაზზე, ხდება 8 ბიტის გადატანა მარჯვნივ და ამ 8 ბიტის გაგზავნა. ამის შემდეგ დარჩენილი 8 ბიტის გაგზავნა. განვიხილავ მაგალითს:
ინფორმაციისთვის : MSB – Most Significant Bit. LSB – Less Significant Bit. MSB ით აღინიშნება პირველი 8 ბიტი (მარცხნიდან), ხოლო LSB ით პირველი 8 ბიტი (მარჯვნიდან).

საჭიროა გავგზავნოთ მისამართი 20 000, რომელიც ორობით სისტემაში არის 0100 1110 0010 0000. ჯერ უნდა გავგზავნოთ MSB და შემდეგ LSB. 16 ბიტიდან ჯერ უნდა მივიღოთ MSB ის ბიტები, ამიტომ ხდება მათი 8 პოზიციით გადატანა.

0100 1110 0010 0000 -> 8 ბიტის გადაწევისას ->  0100 1110

მესამე ხაზზე კი სრულდება შემდეგი ინსტრუქცია (დაპროგრამებაში ეგრეთწოდებული შენიღბვა (masking)) ლოგიკური და’ ს გამოყენებით.

0100 1110 0010 0000 -> შენიღბვა -> 0010 0000

ეს იმას ნიშნავს, რომ 24LC256 ჩიპი მონაცემებს იღებს შემდეგნაირად, ჯერ 0100 1110 და შემდეგ 0010 0000, რომლითაც ის ხვდება, რომ ინფორმაციის მოთავსება იწყება 20 000 დან. ამით ჩვენ მას ვამცნობთ, რომ გვინდა მასში ინფორმაციის ჩაწერა გადაცემულ მისამართზე. და ვაგზავნი კიდევაც ინფორმაციას. ამის შემდეგ უკვე ხდება კომუნიკაციის დასრულება იმით, რომ ჩიპი თავის თავში წერს მიღებულ ინფორმაციას. ასევე არ უნდა დაგავიწყდეთ 5 მილი წამის ოდენობის შეყოვნება, რადგან მოწყობილობამ მოასწროს ინფორმაციის შენახვა. როდესაც არის მომენტები, რომლებშიც მიმდევრობით ხდება ინფორმაციის ჩაწერა, ამ დროს შეყოვნების გარეშე შეიძლება ყველაფერი ცუდად წავიდეს და უცნაური რამე მოხდეს.  

ამის შემდეგ მეორე, წაკითხვის ფუნქცია, მისამართის კონვერტირებას 2 ბაიტიდან იგივე გზით ახდენს 2 ცალ 1 ბაიტში. უგზავნის მოწყობილობას ამ მისამართს და ასრულებს კომუნიკაციას. შემდეგ ითხოვს მოწყობილობისგან ინფორმაციას. Wire.requestFrom() ფუნქციის მეორე პარამეტრი აღნიშნავს თუ რამდენი ბაიტის წაკითხვა არის საჭირო (ჩვენს შემთხვევაში 1 ის). შემდეგ ვამოწმებ არის თუ არა ინფორმაცია I2C ს ხაზზე და ვკითხულობ მას. ამის შემდეგ ფუნქცია აბრუნებს 1 ბაიტ ინფორმაციას.

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


No comments:

Post a Comment