ეს ტუტორიალი მიზნად ისახავს დაწვრილებით ავხსნა თუ რა არის ინტერაპტი და რას წარმოადგენს ის მიკრო კონტროლერში. ჩემი აზრით ეს საოცრად მოქნილი და დახვეწილი მექანიზმია, რომელიც საშუალებას გვაძლევს დარწმუნებულები ვიყოთ იმაში, რომ ყველაფრის შემოწმება ჩვენზე არ იქნება დამოკიდებული მიკრო კონტროლერის დაპროგრამებისას.
რა არის ინტერაპტი?
როგორც ვიცით მიკრო კონტროლერებში პროგრამული ბრძანებები მიმდევრობით სრულდება. მიკრო კონტროლერები ურთიერთქმედებენ ფიზიკურ გარე სამყაროსთან. გარე სამყარო კი ხშირად გაუთვლელია, რაც იმას ნიშნავს, რომ რაიმე ცვლილებამ შეიძლება გაუთვალისწინებელი პრობლემები შეუქმნას კოდს, თუ შესაბამისი, წინასწარ გაწერილი, ინსტრუქცია ვერ მოიძებნა.
მაგალითისთვის ავიღოთ მოწყობილობა, რომელიც აცხობს ნამცხვრებს, ხოლო გამომცხვარი ნამცხვრების დათვლის მიხედვით ანგარიშობს მათი გაყიდვიდან მიღებულ შემოსავალს. როდესაც ეს რიცხვი საკმაოდ დიდი გახდება მიკრო კონტროლერისთვის საანგარიშოდ, მაშინ შეიძლება ამ ყველაფერმა დიდი დრო წაიღოს და ნამცხვრები დამწვარი გამოვიდეს. სწორედ ამ დროს თამაშობს ინტერაპტი დიდ როლს - შეაჩეროს პროგრამის შესრულება, გააკეთოს რაღაც დავალება, და განაგრძოს პროგრამის შესრულება იმ ადგილიდან სადაც შეწყვიტა. ჩვენ გამოგონილ მოწყობილობას რომ დავუმატოთ ინტერაპტი, მაშინ ის მიკრო კონტროლერს “ეტყვის” როდის უნდა გამოიღოს ნამცხვარი რომ არ დაიწვას და გამოღების შემდეგ ისევ გააგრძელებს ანგარიშს.
მოქმედების თეორია
ვნახოთ პროგრამა, რომელიც განკუთვნილია 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-ზე სიგნალის მნიშვნელობა შეიცვლება. ვნახოთ ქვემოთ მოცემული სურათი და მასზე მწვანედ აღნიშნული ორი პინი
გარე ინტერაპტები საკმაოდ მძლავრი იარაღია ჩვენს ხელში, მათი კონფიგურაცია ისე შეიძლება, რომ ინტერაპტის კოდი გამოძახებული იქნას ოთხ გარემოებაში, ესენია:
1. როდესაც პინი წაიკითხავს ლოგიკურ ნულს (LOW)
2. როდესაც ერთი ლოგიკური სიგნალი შეიცვლება მეორეთი
3. როდესაც მაღალი (HIGH) სიგნალიდან გადადის დაბალ (LOW) სიგნალზე
4. როდესაც დაბალი (LOW) სიგნალიდან გადადის მაღალ (HIGH) სიგნალზე
გარე ინტერაპტები იყენებენ 3 რეგისტრს, რომლებიც შეგიძლიათ იპოვოთ “external interrupts” სექციაში, მიკრო კონტროლერის მონაცემთა ფურცელში. პირველი რეგისტრია EICRA (External Interrupt Control Register A). და მასში, ჩვენთვის საინტერესოა მხოლოდ პირველი 4 ბიტი.
ISCnn-ის მნიშვნელობების ცვლით “ვეუბნებით” მიკრო კონტროლერს, რა გარემოების დროს უნდა შეასრულოს ინტერაპტის კოდი:
ISCn1
|
ISCn0
| |
0
|
0
|
როდესაც პინზე არის ლოგიკური ნულიანი
|
0
|
1
|
როდესაც პინზე არსებული სიგნალი შეიცვლება
|
1
|
0
|
როდესაც სიგნალი იცვლება ‘1’-დან ‘0’-კენ
|
1
|
1
|
როდესაც სიგნალი იცვლება ‘0’-დან ‘1’-კენ
|
ჩვენი მეორე რეგისტრია EIMSK (External Interrupt Mask Register)
ხოლო მესამე რეგისტრია EIFR (External Interrupt Flag Register)
ამ სამი რეგისტრის გამოყენებით კი ქვემოთ არის მოცემული კოდი:
#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 იარლიყით არის მონიშნული თითქმის ყველა პინი (კვების გარდა).
ამ შემთხვევაში ჩვენს განკარგულებაშია 5 რეგისტრი, რომლიდანაც იმართება ამ ინტერაპტების ქცევა. ესენია
Pin Change Interrupt Control Register
Pin Change Interrupt Flag Register
Pin Change Mask Register 0
Pin Change Mask Register 1
Pin Change Mask Register 2
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-ის ასანთებათ. როგორც ქვემოთ მოცემულ სურათზეა მოცემული, თუ დააკვირდებით, ღილაკების უკან თითოეულ ღილაკს შეესაბამება (მარჯვნიდან) წითელი, ცისფერი და ყვითელი სადენი. როდესაც ღილაკს დავაჭერთ ხელს ინტერაპტი აქტიურდება და ინთება შესაბამისი ფერის დიოდი. მარჯვენა პლანზე.
მესამე ხაზზე საკონტროლო რეგისტრში ვუთითებთ, რომ PCMSK2 რეგისტრში არსებული პინებისთვის გვინდა ინტერაპტის სერვისი. შემდეგ PCMSK2 რეგისტრში ვუთითებთ კერძოდ რომელი PCINTx პინისთვის გვინდა ამ სერვისის გააქტიურება.
ამის შემდეგ კი განვსაზღვრავთ კოდს, რომელიც ინტერაპტის გააქტიურებისას ამოქმედდება. ბოლოს კი ვიდეო დემონსტრაცია
საერთო ჯამში ინტერაპტი საკმაოდ მოქნილი იარაღია. ეს სერვისი არამარტო პინების მდგომარეობის ცვლილების დროს შეიძლება იქნას გამოყენებული, არამედ კიდევ ბევრ სხვა პერიფერიებთან საურთიერთობოდ. ჩემს მიერ მოყვანილ კერძო მაგალითში კი loop ში აღარ მოგვიწევს კოდის დაწერა, რომელიც გამუდმებით შეამოწმებს ღილაკების მდგომარეობას. უბრალოდ ინტერაპტი იზრუნებს ამაზე!
No comments:
Post a Comment