Saturday, October 4, 2014

AVR-ის მთვლელები/ტაიმერი - შესავალი

timer-icon-300x3001.png


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

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


ტაიმერები როგორც რეგისტრი
ტაიმერი წარმოადგენს ერთგვარ რეგისტრს. ამ რეგისტრის მნიშვნელობა კი იზრდება ან მცირდება. ეს პროცესი კი ავტომატურად მიმდინარეობს მიკრო პროცესორში. AVR ში ძირითადად გხვდება 8 და 16 ბიტიანი ტაიმერები, რაც იმას ნიშნავს, რომ რეგისტრი შესაბამისად არის 8, ან 16 ბიტის “სიგანის”. ეს ასევე იმასაც ნიშნავს, რომ 8 ბიტიან ტაიმერს შეუძლია 256 (2^8)-ჯერ ჩამოკრას - 0-დან 255-მდე, როგორც სურათზეა ნაჩვენები, ოღონდ ეს ყველაფერი ორობითში.
Generic Forms for AVR tutorial blog.png
შესაბამისად, 16 ბიტიან ტაიმერს შეუძლია 65536-ჯერ ჩამოკრას (2^16), ნულიდან 65535-მდე. ტაიმერები შეიძლება განვიხილოთ, როგორც მთვლელები, რაც სავსებით ნორმალურია. საინტერესოა რა ხდება, როდესაც ტაიმერი დაითვლის მაქსიმალურამდე? პროგრამა შეჩერდება? არა, ტაიმერი განულდება - დაუბრუნდება საწყის, ნულოვან მნიშვნელობას და თავიდან დაიწყებს თვლას, ამას ტაიმერის გადატვირთვა (overflow) ეწოდება. ახლა კი მაგალითისთვის ავიღოთ AtMega32, რომელშიც 3 განსხვავებული ტაიმერი გვაქვს, რომელთაგან პირველი და მესამე 8 ბიტიანია, ხოლო მეორე 16-იანი.


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


რას უდრის დროის პერიოდი? ყველამ იცის, რომ ის ტოლია 1 შეფარდებული სიხშირესთან, ანუ სიხშირის შებრუნებულ სიდიდეს. ახლა კი ჩვენ გვაქვს დიოდი, რომელიც გვინდა ყოველ 10 მილი წამში ერთხელ ავაციმციმოთ, ეს კი მეორე მხრივ იმას ნიშნავს, რომ ციმციმის სიხშირე 100 ჰერცი უნდა იყოს. ყველაფრის გამოსათვლელად, ასევე გვინდა ვიცოდეთ თუ რა სიხშირეზე მუშაობს მიკრო პროცესორი. სტანდარტულად ეს სიხშირე არის 16, 8, 4 ან 1 მეგაჰერცი (8 ბიტიანი სტანდარტული მიკრო პროცესორებისთვის, გაცილებით მძლავრ მიკრო პროცესორებში სიხშირე ერთი ან რამდენიმე ასეული მეგაჰერცის ტოლიც შეიძლება იყოს). დასაწყისისთვის ავიღოთ 1 მეგაჰერცი, რომელიც შიდა მიკრო პროცესორში არსებული შიდა კვარცის გამოყენებით მიიღწევა ხოლმე, ხშირად.


მიკრო პროცესორი ახლა იწყებს მუშაობას, და შესაბამისად ყველა ტაიმერის მნიშნვნელობა ნულის ტოლია. ნულად ერთი, რომ გახდეს ტაიმერის მნიშვნელობა საჭიროა ერთი იმპულსი, რომელიც იმ სიხშირით მეორდება, რა სიხშირეზეც მუშაობს მიკრო პროცესორი (ჩვენს შემთხვევაში 1 მეგაჰერცზე). დროის პერიოდი, რაც ერთ თვლას სჭირდება გამოდის, რომ არის 1/1(მეგაჰერცთან) = 1/1,000,000 = 0.000,000,1 წამი. რადგან ჩვენი სასურველი ინტერვალი არის მილიწამებში (10 მილიწამი) გადავიყვანოთ მიღებული პერიოდი ამ განზომილებაში, რაც = 0.000,1 მილი წამს. მიღებული მნიშვნელობა გვეუბნება, რომ ტაიმერის თითოეული თვლა, სულ რაღაც 0.000,1 მილი წამს უნდება.


ახლა დავუბრუნდეთ ჩვენს პირობას. 10 მილი წამი შეიძლება ძალიან მცირე დრო იყოს. შედარებისთვის გეტყვით, რომ ადამიანი თვალის დახამხამებას 400 მილიწამს ანდომებს, რაც 40-ჯერ მეტი დროა, ვიდრე ჩვენი დიოდის ციმციმის პერიოდი. მეორე მხრივ კი 10 მილი წამი საკმაოდ დიდი დროა ისეთი მიკრო კონტროლერისთვის, რომელიც ხულ რაღაც 1 მეგაჰერცზე მუშაობს და 0.000,1 მილი წამის სიზუსტით არჩევს დროს. სიმარტივისთვის გამოვიყენებ საკმაოდ მარტივ ფორმულას, რომელიც გვეუბნება რამდენჯერ უნდა დაითვალოს მთვლელმა, რომ სასურველი პერიოდი მივიღოთ


Untitled.png


ახლა ისღა დაგვრჩა ფორმულით ვისარგებლოთ. ჩვენი სასურველი პერიოდი არის 10 მილი წამი, ხოლო 1 თვლის პერიოდი = 0.000,1 მილი წამს. გამოთვლით მივიღებთ, რომ ტაიმერმა უნდა დაითვალოს 39,999-ჯერ, რომ 10 მილი წამი გავიდეს. საოცარია, მაგრამ ეს მართლაც ასეა. ამ შედეგის მისაღწევად, ფაქტია, რომ 8 ბიტიან მთვლელს ვერ გამოვიყენებთ, რადგან მისი მაქსიმალური მნიშვნელობა შეიძლება მხოლოდ 255-ს გაუტოლდეს, ჩვენ კი 39,999 დათვლა გვინდა. ამისთვის 16 ბიტიანი ტაიმერი დაგვჭირდება, რომელსაც 65,535-მდე შეუძლია დაითვალოს. ამ ჯერზე პრობლემა თითქოს გადაწყვეტილია, მაგრამ არც ისე მოსახერხებელი გადაწყვეტილება მივიღეთ. ამ ეტაპზე საჭირო ხდება prescaler-ის კონცეფციის გარჩევა, რაც იგივეა, რაც გამყოფი (ყოველ შემთხვევაში მე ასე აღვიქვავ, და ასეც მოვიხსენიებ.


Prescaler
გამყოფი საკმაოდ მოქნილი მექანიზმია არამარტო ტაიმერებში და მთვლელებში, არამედ მიკრო კონტროლერის სხვა პერიფერიალებშიც. ახლა ვთქვათ ჩვენი მიკრო კონტროლერი მუშაობს, მაგალითად, 4 მეგაჰერცზე. თუ გამოვიყენებთ 16 ბიტიან ტაიმერს, მაშინ მაქსიმალური პაუზა/დრო რომლის აღქმის საშუალებაც გვექნება არის 16.384 მილი წამი. ეს კი საკმაოდ უხერხულ სიტუაციას ქმნის, როდესაც 0.016 წამზე მეტ პაუზას ვერ გავაკეთებთ, სხვადასხვა მიზნით. ტექნიკურად პაუზის გაკეთება (16 მილი წამზე დიდი) შესაძლებელია, მაგრამ ეს დამატებით ხაზებს მოითხოვს კოდში და შეცდომის რისკიც იზრდება. სწორედ ასეთ შემთხვევებში გვჭირდება prescaler ის გამოყენება.
prescaler ის მთავარი “იარაღი” ის არის, რომ ის ახდენს მიკრო კონტროლერის სიხშირის შემცირებას, ისე, რომ ეს თვითონ მიკრო კონტროლერს არ ანელებს. შეიძლება ცოტა ბუნდოვანია, მაგრამ ეს მართლაც ასეა. prescaler-ის გამოყენებით, რეალურად პროცესორის სიხშირეს არ ვამცირებთ, უბრალოდ პროცესორის სიხშირეს ვიყენებთ და ვყოფთ, რომ მივიღოთ უფრო დაბალი სიხშირე. ეს ყველაფერი კი სპეციალური რეგისტრის დახმარებით ხდება, რის საშუალებითაც შეგვიძლია სიხშირის შემცირება, რომ გამოვიყენოთ ტაიმერისთვის. ამ რეგისტრს გავარჩევთ მოგვიანებით. მანამდე კი მსურს, გითხრათ რა ხდება prescaler-ის გამოყენების დროს.


რადგან არსებობს prescaler-ის მექანიზმი, ეს სულაც არ ნიშნავს იმას, რომ ის სრულყოფილია და მისი გამოყენება თავისუფლად არის შესაძლებელი. რაც უფრო იზრდება პაუზის ხანგრძლივობა, მით უფრო მცირდება გარჩევადობა. მაგალითისთვის ავიღოთ, რომ 4 მეგაჰერციანი პროცესორის სამუშაო სიხშირე დავიყვანეთ 500 კილოჰერცამდე (0.5 მეგაჰერცი). ეს პარალელურად იმას ნიშნავს, რომ 16.384 მილი წამიანი მაქსიმალური პერიოდი (რაც იყო 4 მეგაჰერციანი სიხშირის დროს) გაიზარდა 8-ჯერ და გაუტოლდა 131.072 მილი წამს. თუ კარგად გავიაზრებთ გარჩევადობის დროც გაიზარდა 0.00025 მილი წამიდან 0.002 მილიწამამდე. ტექნიკურად კი, გარჩევადობა შემცირდა, რაც პარალელურად იწვევს სიზუსტის გაუარესებასაც. აქვე ვნახოთ მაგალითი. დავუშვათ, რომ გვინდა 0.2305 მილი წამის გაზომვა, ზუსტად! ძველ პირობებში ეს სავსებით შესაძლებელია, რადგან 0.2305 / 0.00025 (გარჩევადობა არის 0.00025 მილი წამი) = 922. მაგრამ ახალ პირობებში, როდესაც გარჩევადობა არის 0.002 ამ პაუზას ასე ზუსტად ვეღარ გავზომავთ - 0.2305 / 0.002 (ახალი გარჩევადობა) = 115.25. ახალ სიხშირეზე აწყობილ ტაიმერს შეუძლია გაზომოს 0.2300 და შემდეგ 0.2320.


prescaler-ის არჩევა
მაგალითისთვის ავიღოთ, რომ გვინდა 184 მილი წამის პაუზის გაკეთება. მიკრო კონტროლერის სიხშირე იყოს ისევ 4 მეგაჰერცი. AVR-ის მიკრო კონტროლერების prescaler შეიძლება იყოს 8; 64; 256; 1024. თუ ავირჩევთ 8-ს როგორც prescaler-ად, მაშინ ტაიმერის მოქმედი სიხშირე იქნება 4 / 8 მეგაჰერცი, რაც 500 კილოჰერცის ტოლია. შესაბამისად თუ ავირჩევთ:


  • 8     -    500 კილოჰერცი
  • 64     -     62.5 კილოჰერცი
  • 128    -    15.625 კილოჰერცი
  • 1024    -    3906.25 კილოჰერცი
prescaler-ის გამოყენებისას ტაიმერის თვლის რიცხვი არ იზრდება. არც 8 და არც 16 ბიტიანი ტაიმერის თვის რიცხვი. უბრალოდ, იზრდება გარჩევადობა - დრო ერთიდან მეორე თვლამდე.


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


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

მაგალითისთვის ავიღოთ ტაიმერის გადატვირთვა - როდესაც ის დაითვლის ბოლოჯერ და იწყებს თავიდან. ჩვენ შესაძლებლობაში არის ჩავრთოთ ის ინტერაპტი, რომელიც გააქტიურდება რომელიმე ტაიმერის გადატვირთვისას. ახლა, როდესაც გადატვირთვა მოხდება მიკრო კონტროლერი შეასრულებს შესაბამის რუტინას, რომელსაც ეწოდება ISR (Interrupt Service Routine). ის, თუ რა მოხდება ამ ინტერაპტის გააქტიურებისას ეს დამოკიდებულია ჩვენზე.

No comments:

Post a Comment