Tuesday, September 9, 2014

ინტერაპტები


IMG_20140909_195015.jpg


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



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


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


მოქმედების თეორია
ვნახოთ პროგრამა, რომელიც განკუთვნილია AtMega168 / AtMega328-თვის.


#include
#include


void setup()
{
 sei();  //Enable Global Interrupts
}


void loop()
{
 //TODO1
}


ISR(INT0_vect)
{
 //TODO2
}


ISR(BADISR_vect)
{
 //TODO3
}


setup ფუნქციაში ამჟამად მოთავსებულია მხოლოდ ერთი ინსტრუქცია - sei(), რომელიც მიკრო კონტროლერს ეუბნება, რომ გააქტიუროს ინტერაპტები. ხოლო რაც შეეხება ISR(x_vect) არის ზოგადი ფუნქცია, როდესაც x ვექტორს გამოიძახებს მიკრო კონტროლერი. ეს კოდი კი ეუბნება კონტროლერს ამ ვექტორის გამოძახების შემდეგ რა ინსტრუქცია შეასრულოს. მიკრო კონტროლერის მონაცემთა ფურცელში ინტერაპტის ვექტორში არ არის დატანილი BADISR წევრი, რომელიც იმ შემთხვევაში შესრულდება, როდესაც კოდი გამოიძახებს იმ ვექტორს, რომელიც არ არსებობს.


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


#include
#include


void setup()
{
 sei();  //Enable Global Interrupts
}


void loop()
{
 cli();
 //Interrupts Are Disabled
 sei();
 //Re-enable Interrupts
}


ISR(INT0_vect)
{
 //TODO2
}


ISR(BADISR_vect)
{
 //TODO3
}


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


volatile uint8_t GloblaVarForInterrupt;


გარე ინტერაპტები
გარე ინტერაპტების განხილვას დავიწყებ AtMega168/328-სთვის, რადგან ეს ჩიპები საკმაოდ გავრცელებულია არდუინოს ეკოსისტემაში. ამ ნაწილის მთავარი თემა არის გარე ინტერაპტები, ეს ინტერაპტები გამოიძახება იმ შემთხვევაში, როდესაც INT0 / INT1-ზე სიგნალის მნიშვნელობა შეიცვლება. ვნახოთ ქვემოთ მოცემული სურათი და მასზე მწვანედ აღნიშნული ორი პინი


imagesB5812G0S.jpg
გარე ინტერაპტები საკმაოდ მძლავრი იარაღია ჩვენს ხელში, მათი კონფიგურაცია ისე შეიძლება, რომ ინტერაპტის კოდი გამოძახებული იქნას ოთხ გარემოებაში, ესენია:


1. როდესაც პინი წაიკითხავს ლოგიკურ ნულს (LOW)
2. როდესაც ერთი ლოგიკური სიგნალი შეიცვლება მეორეთი
3. როდესაც მაღალი (HIGH) სიგნალიდან გადადის დაბალ (LOW) სიგნალზე
4. როდესაც დაბალი (LOW) სიგნალიდან გადადის მაღალ (HIGH) სიგნალზე


გარე ინტერაპტები იყენებენ 3 რეგისტრს, რომლებიც შეგიძლიათ იპოვოთ “external interrupts” სექციაში, მიკრო კონტროლერის მონაცემთა ფურცელში. პირველი რეგისტრია EICRA (External Interrupt Control Register A). და მასში, ჩვენთვის საინტერესოა მხოლოდ პირველი 4 ბიტი.


EICRA Register.PNG
ISCnn-ის მნიშვნელობების ცვლით “ვეუბნებით” მიკრო კონტროლერს, რა გარემოების დროს უნდა შეასრულოს ინტერაპტის კოდი:



ISCn1
ISCn0

0
0
როდესაც პინზე არის ლოგიკური ნულიანი
0
1
როდესაც პინზე არსებული სიგნალი შეიცვლება
1
0
როდესაც სიგნალი იცვლება ‘1’-დან ‘0’-კენ
1
1
როდესაც სიგნალი იცვლება ‘0’-დან ‘1’-კენ


ჩვენი მეორე რეგისტრია EIMSK (External Interrupt Mask Register)
EIMSR.PNG
ხოლო მესამე რეგისტრია EIFR (External Interrupt Flag Register)
EIFR.PNG
ამ სამი რეგისტრის გამოყენებით კი ქვემოთ არის მოცემული კოდი:


#include
#include
int IntPin = 2;  //PCINT0
void setup()
{
 DDRD |= (0<//PD2 is Input
 PORTD |= (1<//Pulled-up

 EIMCRA |= (1<//Any Logic Change
 EIMSK |= (1<//Turn On INT0

 sei();  //Turn On Global Interrupts
}
void loop()
{
   
}
ISR(INT0_vect)
{
 //TODO
}


PIN Change Interrupts
ერთ-ერთი სიახლე Atmega88-დან Atmega328-მდე მიკრო კონტროლერებში არის პინის მდგომარეობის ცვლილების ინტერაპტი. აქაც მოცემულია რამდენიმე რეგისტრი და მათი კონტროლი საშუალებას გვაძლევს (მაგალითისთვის ავიღოთ Atmega168/328) ყველა პინი გავაკონტროლოთ და მათზე ლოგიკური დონის შეცვლის თანავე შევასრულებინოთ კონტროლერს რაღაც კოდი. თუ დააკვირდებით, ყველა პინი მონიშნულია PCINTxx ნომრით მიკრო კონტროლერზე რაც ნიშნავს - Pin Change INTerrupt xx. ვნახოთ სურათი, მხოლოდ აქ მწვანე ხაზებს არ მივაქციოთ ყურადღება, უბრალოდ შევნიშნოთ, რომ PCINTxx იარლიყით არის მონიშნული თითქმის ყველა პინი (კვების გარდა).
imagesB5812G0S.jpg
ამ შემთხვევაში ჩვენს განკარგულებაშია 5 რეგისტრი, რომლიდანაც იმართება ამ ინტერაპტების ქცევა. ესენია


Pin Change Interrupt Control Register
PCICR.PNG
Pin Change Interrupt Flag Register
PCIFR.PNG
Pin Change Mask Register 0
PCMSK0.PNG
Pin Change Mask Register 1
PCMSK1.PNG
Pin Change Mask Register 2
PCMSK2.PNG



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


2. რადგან 8 პინი ინაწილებს ერთ PCINTx ვექტორს ( /რეგისტრს), ინეტრაპტის დაფიქსირებისას მოგიწევთ მოძებნა იმ გარემოების, თუ რომელმა მოვლენამ გამოიწვია ამ რეგისტრის ინტერაპტის გააქტიურება. გამოსავალი აქაც იგივეა - დაიმახსოვროთ პინების სტატიკური მდგომარეობა და შემდეგ ინტერაპტის გააქტიურებისას შეადაროთ ახალი მდგომარეობა ძველს.


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


#include
#include
void setup()
{
 DDRD = (0<
 DDRB = (1<
 PORTD = 0b11111111;

 PCICR |= (1<
 PCMSK2 |= (1<
 sei();
}
void loop()
{

}
ISR(PCINT2_vect)
{
 byte pat1 = 0b11111011;
 byte pat2 = 0b11110111;
 byte pat3 = 0b11101111;

 if(PIND == pat1)
    PORTB |= (1<
 if(PIND == pat2)
    PORTB |= (1<
 if(PIND == pat3)
    PORTB |= (1<
}


ახლა დავიწყოთ კოდის გარჩევა და ვფიქრობ ყველაფერი ნათელი გახდება. პირველ სამი ხაზი გასაგები უნდა იყოს. თუ არა მაშინ იხილეთ ეს ტუტორიალი. PORTD რეგისტრის PD2, PD3 და PD4 პინებს ვიყენებთ ინტერაპტის სერვისის დემონსტრაციისთვის, ხოლო PB0, PB1 და PB2 რეგისტრებს შესაბამისი LED-ის ასანთებათ. როგორც ქვემოთ მოცემულ სურათზეა მოცემული, თუ დააკვირდებით, ღილაკების უკან თითოეულ ღილაკს შეესაბამება (მარჯვნიდან) წითელი, ცისფერი და ყვითელი სადენი. როდესაც ღილაკს დავაჭერთ ხელს ინტერაპტი აქტიურდება და ინთება შესაბამისი ფერის დიოდი. მარჯვენა პლანზე.


IMG_20140909_195015.jpg
მესამე ხაზზე საკონტროლო რეგისტრში ვუთითებთ, რომ PCMSK2 რეგისტრში არსებული პინებისთვის გვინდა ინტერაპტის სერვისი. შემდეგ PCMSK2 რეგისტრში ვუთითებთ კერძოდ რომელი PCINTx პინისთვის გვინდა ამ სერვისის გააქტიურება.


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



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



No comments:

Post a Comment