STM32: ძირითადი ტაიმერები. STM32F3

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

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

#include "stm32f30x_gpio.h" #include "stm32f30x_rcc.h" #include "stm32f30x_tim.h" #include "stm32f30x.h" /*******************************************************************/ TIM_TimeBaseInitTypeDef ტაიმერი; /*******************************************************************/

მინიმალური ტაიმერის ინიციალიზაცია ასე გამოიყურება. სხვათა შორის, ამავდროულად დავაყენებთ კონტროლერის ერთ-ერთ ფეხს გამომავალი რეჟიმში მუშაობისთვის. საჭიროა მხოლოდ LED-ის მოციმციმე 😉

/*******************************************************************/ void initAll() ( // Clocking - სად მის გარეშე RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // ჩვენ გვაქვს ლურჯი LED ამ პინზე (STM32F3Discovery) gpio.GPIO_Mode = GPIO_Mode_OUT; gpio.GPIO_Pin = GPIO_Pin_8; gpio.GPIO_OType = GPIO_OType_PP; gpio.GPIO_Speed ​​= GPIO_Speed_50MHz; GPIO_Init(GPIOE, & gpio); // და აქ არის დიდი ხნის ნანატრი TIM2 ტაიმერის პარამეტრი TIM_TimeBaseStructInit(&timer) ; ტაიმერი.TIM_Prescaler = 7200; ტაიმერი.TIM_პერიოდი = 20000 ; TIM_TimeBaseInit(TIM2 და ტაიმერი); /*******************************************************************/

აქ ღირს ყურადღება მიაქციოთ ორ რიცხვს, რომლებიც არსაიდან მოდის - 7200 და 20000 . ახლა მოდით გავარკვიოთ რა არის ეს 😉 ჩემი ტაიმერი სიხშირით არის დაკვრა 72 MHz. პრესკალერი, რომელიც ასევე ცნობილია როგორც პრესკალერი, საჭიროა ამ სიხშირის გასაყოფად) ამრიგად, ჩვენ ვიღებთ 72 MHz / 7200 = 10 kHz. ასე რომ, ტაიმერის ერთი ნიშანი შეესაბამება (1/10000) წამი, რაც უდრის 100 მიკროწამები. ქრონომეტრის პერიოდი არის მნიშვნელობა, დათვლის შემდეგ, რომელზედაც პროგრამა მიფრინავს შეფერხების დამმუშავებელთან ტაიმერის გადინებაზე. ჩვენს შემთხვევაში, ტაიმერი იკლებს მდე 20000 , რაც შეესაბამება (100 * 20000) μsან 2 წამი. ანუ, LED (რომელსაც ჩართავთ და გამორთავთ შეფერხების დამმუშავებელში) აციმციმდება წერტილით 4 წამი (2 წამი, 2 წამი არ იწვის =)). ახლა ამით ყველაფერი ნათელია, ჩვენ ვაგრძელებთ ...

ფუნქციაში მთავარი ()ჩვენ მოვუწოდებთ ინიციალიზაციის ფუნქციას, ასევე ჩართავთ შეფერხებებს და ტაიმერს. ციკლში ხოლო (1)კიდევ ნაკლები კოდი - ის უბრალოდ ცარიელია 😉

/*******************************************************************/ int main() (__enable_irq() ; initAll() ; TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE) ; TIM_Cmd(TIM2, ENABLE) ; NVIC_EnableIRQ(TIM2_IRQn) ; while (1)) /*******************************************************************/

ყველაფერი, რჩება რამდენიმე სტრიქონის დაწერა შეფერხების დამმუშავებლისთვის და სამუშაო დასრულებულია:

/*******************************************************************/ void TIM2_IRQHandler() ( TIM_ClearITPendingBit(TIM2, TIM_IT_Update) ; if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_8) == 1 ) ( GPIO_ResetBits(GPIOE_ResetBits(GPIOE,GPIO) else,GPIO)(GPIOE_8BiOs); /*******************************************************************/

პროგრამის კონტროლერში ჩართვის შემდეგ, ჩვენ ვაკვირდებით მოციმციმე ლურჯ LED-ს, ამიტომ პროგრამა გამართულად მუშაობს! პრინციპში, ეს ყველაფერი დღესაა, ასეთი მოკლე სტატია აღმოჩნდა)

ჩვენ უკვე გადავხედეთ SysTick ტაიმერი, რომელიც Cortex ბირთვის ნაწილია. თუმცა, ეს არ მთავრდება. stm32-ში ბევრი ტაიმერია და ისინი განსხვავებულია. მიზნიდან გამომდინარე, თქვენ უნდა აირჩიოთ ერთი ან სხვა ტაიმერი:

  • SysTick;
  • ზოგადი დანიშნულების ტაიმერები - TIM2, TIM3, TIM4, TIM15, TIM16, TIM17;
  • გაფართოებული ტაიმერი (ინგლისური advanced timer) - TIM1;
  • მცველი ტაიმერი.

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

ტაიმერები მოდის სხვადასხვა ბიტის ზომებში: მაგალითად, SysTick არის 24-ბიტიანი და ყველა ტაიმერი, რომელიც გვაქვს ქვაში, არის 16-ბიტიანი (ანუ მათ შეუძლიათ დათვლა 2 16 = 65535-მდე), გარდა WatchDog-ისა. გარდა ამისა, თითოეულ ტაიმერს აქვს არხების გარკვეული რაოდენობა, ანუ, ფაქტობრივად, მას შეუძლია იმუშაოს ორზე, სამზე და ა.შ. პულსის სიგანის მოდულაცია - რაზეც მოგვიანებით ვისაუბრებთ) და სხვა. გარდა ამისა, მათ შეუძლიათ შექმნან შეფერხებები ან მიმართონ სხვა მოდულებს (მაგალითად, DMA - Direct Memory Access) სხვადასხვა მოვლენებზე:

  • overflow (ინგლისური overflow);
  • სიგნალის დაჭერა (ინგლ. input capture);
  • შედარებით (ინგლისური გამომავალი compare);
  • event-trigger (ინგლისური ღონისძიების გამომწვევი).

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

სიგნალის დაჭერა

ეს რეჟიმი კარგად არის შესაფერისი იმპულსების პერიოდის გასაზომად (ან მათი რიცხვი, ვთქვათ, წამში). როდესაც პულსი მოდის MK გამოსავალზე, ტაიმერი წარმოქმნის შეფერხებას, საიდანაც შეგვიძლია ამოიღოთ მიმდინარე მრიცხველის მნიშვნელობა (TIM_CCRx რეგისტრიდან, სადაც x არის არხის ნომერი) და შევინახოთ იგი გარე ცვლადში. შემდეგ მოვა შემდეგი პულსი და მარტივი გამოკლებით მივიღებთ „დროს“ ორ პულსს შორის. თქვენ შეგიძლიათ დაიჭიროთ პულსის ორივე წინა და უკანა კიდეები, ან თუნდაც ორივე ერთდროულად. რატომ არის ეს საჭირო? ვთქვათ, თქვენ გაქვთ მაგნიტი, რომელიც ბორბალზე დისკზე გაქვთ მიმაგრებული, ხოლო ჰოლის ეფექტის სენსორი ველოსიპედის ჩანგალზე. შემდეგ, როდესაც ატრიალებთ ბორბალს, თქვენ მიიღებთ იმპულსს ყოველ ჯერზე, როცა საჭეზე მაგნიტი იმავე სიბრტყეშია, როგორც ჰოლის სენსორი. იმის ცოდნა, თუ რა მანძილი დაფრინავს მაგნიტი ერთ რევოლუციაზე და დრო, შეგიძლიათ გამოთვალოთ მოძრაობის სიჩქარე.

ასევე არსებობს PWM გადაღების რეჟიმი, მაგრამ ეს უფრო სპეციალური გზაა ტაიმერის დასაყენებლად, ვიდრე მუშაობის ცალკე რეჟიმი: ერთი არხი იჭერს ამომავალ კიდეებს, ხოლო მეორე იჭერს დაცემას. შემდეგ პირველი არხი ამოიცნობს პერიოდს, ხოლო მეორე - შევსებას.

შედარების რეჟიმი

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

PWM გენერირების რეჟიმი

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

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

მკვდარი დროის რეჟიმი

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

კურსში ჩვენ გამოვიყენებთ მხოლოდ "სიგნალის დაჭერას" და "pwm გენერაციას".

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

Რისთვის არის?
ამ რეჟიმის გამოყენებით შეგიძლიათ გაზომოთ პულსის ხანგრძლივობა ან სიგნალის პერიოდი.

STM32 გადაღების რეჟიმს აქვს რამდენიმე ფუნქცია:

  • შესაძლებლობა აირჩიოს რომელი ფრონტი იქნება აქტიური
  • შეყვანის სიგნალის სიხშირის შეცვლის შესაძლებლობა პრესკალერის გამოყენებით (1,2,4,8)
  • გადაღების თითოეულ არხს აქვს ჩაშენებული შეყვანის ფილტრი
  • დაჭერის სიგნალის წყარო შეიძლება იყოს სხვა ტაიმერი
  • თითოეული არხისთვის მოცემულია ორი დროშა, პირველი დაყენებულია, თუ მოხდა გადაღება, მეორე, თუ დაჭერა მოხდა პირველი დროშის ნაკრებით

რეგისტრები გამოიყენება გადაღების რეჟიმის დასაყენებლად. CCMR1(1 და 2 არხისთვის) და CCMR2(3 და 4-ისთვის), ასევე რეგისტრები CCER, DIER.

მოდით უფრო ახლოს მივხედოთ რეგისტრაციის ბიტის ველებს CCMR2, რომლებიც პასუხისმგებელნი არიან 4 ტაიმერის არხის დაყენებაზე, ჩვენ დავაკონფიგურირებთ მაგალითში. ასევე მინდა აღვნიშნო, რომ იმავე რეესტრში არის ბიტის ველები, რომლებიც გამოიყენება ტაიმერის შედარების რეჟიმში დაყენებისას.

CC4S- განსაზღვრავს მეოთხე არხის მიმართულებას (შემავალი ან გამომავალი). არხის, როგორც შეყვანის კონფიგურაციისას, ასახავს მას გადაღების სიგნალს

  • 00 - არხი მუშაობს როგორც გამომავალი
  • 01 - არხი მუშაობს როგორც შემავალი, გადაღების სიგნალი - TI4
  • 10 - არხი მუშაობს როგორც შემავალი, გადაღების სიგნალი - TI3
  • 11 - არხი მუშაობს როგორც შემავალი, გადაღების სიგნალი - TRC
IC4PSC– განსაზღვრეთ გაყოფის ფაქტორი დაჭერის სიგნალისთვის
  • 00 - გამყოფი არ არის გამოყენებული, IC1PS დაჭერის სიგნალი გენერირებულია თითოეული მოვლენისთვის
  • 01 - დაჭერის სიგნალი იქმნება ყოველი მეორე მოვლენისთვის
  • 10 - დაჭერის სიგნალი წარმოიქმნება ყოველი მეოთხე მოვლენისთვის
  • 11 - დაჭერის სიგნალი წარმოიქმნება ყოველი მერვე მოვლენისთვის
IC4F- შექმნილია შეყვანის ფილტრის დასარეგულირებლად, გარდა ნიმუშების რაოდენობისა, რომლის დროსაც მიკროკონტროლერი არ პასუხობს შეყვანის სიგნალებს, ასევე შეგიძლიათ დაარეგულიროთ შერჩევის სიჩქარე. არსებითად, ჩვენ ვარეგულირებთ დაყოვნების დროს იმ მომენტიდან, როდესაც ზღვარი მიდის "აღიარებულ" ნიმუშამდე.

ახლა მოდით გადახედოთ რეესტრს CCER.

CC4E- ჩართავს/გამორთავს გადაღების რეჟიმს.
CC4P- განსაზღვრავს წინა მხარეს, რომლის გასწვრივ მოხდება დაჭერა, 0 - წინა, 1 - უკანა.

და დარეგისტრირდით DIER.

CC4DE- საშუალებას გაძლევთ ჩამოაყალიბოთ მოთხოვნა DMA-ზე.
CC4IE- ჩართავს გადაღების შეწყვეტას.

დაჭერის განხორციელების შემდეგ, წარმოიქმნება დაჭერის მოვლენა, რომელიც ადგენს შესაბამის დროშას. ამან შეიძლება გამოიწვიოს შეფერხების გენერირება და მოთხოვნის წარმოქმნა DMAთუ ისინი დაშვებულია რეესტრში DIER. გარდა ამისა, გადაღების მოვლენის გენერირება შესაძლებელია პროგრამულად, მოვლენის გენერირების რეესტრში ბიტის ველის დაყენებით EGR:

ბიტი ველები CC1G, CC2G, CC3G და CC4Gსაშუალებას გაძლევთ შექმნათ ღონისძიება შესაბამის გადაღება/შედარების არხში.

Ჰო მართლა, CCR1, CCR2, CCR3 და CCR4- დაჭერის რეგისტრები, რომლებიც ინახავს ტაიმერის მნიშვნელობას დაჭერის სიგნალზე.

დაჭერის სიგნალის წარმოქმნის კონტროლის მიზნით, რეესტრში სრთითოეულ არხს აქვს ორი დროშა.

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

ახლა გამოვიყენოთ მიღებული ცოდნა პრაქტიკაში, სიგნალის გენერატორიდან TIM5_CH4 შესასვლელამდე გამოვიყენებთ სინუსოიდს 50 ჰც სიხშირით და შევეცდებით გავზომოთ მისი პერიოდი. პროცესის დაჩქარების მიზნით, გირჩევთ გამოიყენოთ DMA. MK-ის რომელი გამომავალი შეესაბამება TIM5-ის მე-4 არხს, შეგიძლიათ იხილოთ MK-ის მონაცემთა ფურცელში განყოფილებაში. Pinouts და pin აღწერა.

ამისთვის DMAსაჭიროა რეგისტრაციის მისამართი CCR4, აი, როგორ მოვძებნოთ იგი. ჩვენ ვხსნით RM0008და მაგიდაზე დაარეგისტრირეთ საზღვრის მისამართებიიპოვნეთ TIM5-ის საწყისი მისამართი.


რეგისტრაცია ოფსეტური CCR4შეგიძლიათ იხილოთ იმავე დოკუმენტში ქვემოთ დაარეგისტრირე რუკა.

#include "stm32f10x.h" #define TIM5_CCR4_Address ((u32)0x40000C00+0x40) #define DMA_BUFF_SIZE 2 uint16_t buff;//Buffer uint16_t volatile T; void DMA2_Channel1_IRQHandler (ბათილი) ( T = (buff > buff) ? (buff - buff) : (65535+ buff - buff); DMA2->IFCR |= DMA_IFCR_CGIF1; ) void Init_DMA(void) ( RCC->AHBH2EN | ; //პირველი DMA მოდულის ჩართვა DMA2_Channel1->CPAR = TIM5_CCR4_Address; //მიუთითეთ პერიფერიული მისამართი - ADC კონვერტაციის შედეგის რეგისტრი ჩვეულებრივი არხებისთვის DMA2_Channel1->CMAR = (uint32_t)buff; //დააყენეთ მეხსიერების მისამართი მასივის საბაზისო მისამართი RAM-ში DMA2_Channel1 ->CCR &= ~DMA_CCR1_DIR; // მიუთითეთ მონაცემთა გადაცემის მიმართულება, პერიფერიიდან მეხსიერებაში DMA2_Channel1->CNDTR = DMA_BUFF_SIZE; //გადატანილი მნიშვნელობების რაოდენობა DMA2_CRChannel1- = ~DMA_CCR1_PINC; //არ გაზარდოთ პერიფერიული მისამართი ყოველი DMA2_Channel1 გადაცემის შემდეგ ->CCR |= DMA_CCR1_MINC; //მეხსიერების მისამართის გაზრდა ყოველი გადაცემის შემდეგ DMA2_Channel1->CCR |= DMA_CCR1_PSIZE_0; //პერიფერიული ზომა DMA21_6 CCR |= DMA_CCR1_MSIZE_0; //მეხსიერების მონაცემთა ზომა - 16 ბიტი DMA2_Channel1- >CCR |= DMA_CCR1_PL; //პრიორიტეტი ძალიან მაღალია DMA2_Channel1->CCR |= DMA_CCR1_CIRC; //ჩართეთ DMA ციკლურ რეჟიმში DMA2_Channel1->CCR |= DMA_CCR1_TCIE;//ჩართეთ შეფერხება გადაცემის ბოლოს DMA2_Channel1->CCR |= DMA_CCR1_EN; // ჩართეთ პირველი DMA არხი ) int main(void) ( Init_DMA(); // ჩართეთ A პორტის დაკვრა, ალტერნატიული ფუნქციები და ტაიმერი RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN; RCC->APB1ENR |1; EN RCCT_APIM >PSC = 56000-1;//ახალი სიხშირე 1Khz TIM5->CCMR2 |= TIM_CCMR2_CC4S_0;//აირჩიეთ TI4 TIM5_CH4 TIM5->CCMR2 &= ~(TIM_CCMR2_IC4F | TIM_PSMR2_Dont' და/_IC გამოიყენეთ TIM5- >CCER &= ~TIM_CCER_CC4P;//აირჩიეთ გადაღება ამომავალ კიდეზე TIM5->CCER |= TIM_CCER_CC4E;//ჩართეთ გადაღების რეჟიმი მე-4 არხისთვის TIM5->DIER |= TIM_DIER_CC4DE;//მოთხოვნის ჩამოყალიბების ნება DMA-ზე //TIM5 ->DIER |= TIM_DIER_CC4IE; //გადაღების შეფერხების ჩართვა TIM5->CR1 |= TIM_CR1_CEN; //ჩართეთ მრიცხველი //NVIC->ISER |= NVIC_ISER_SETENA_18; //TIM5 შეწყვეტა NVIC->IC_TE2 NVIC->ISER_4 |= ; //DMA შეწყვეტა ხოლო (1) ( ) )

STM32-ს აქვს მრავალი ძალიან მოსახერხებელი და მოქნილი ტაიმერი დასაყენებლად. ყველაზე ახალგაზრდა მიკროკონტროლერსაც კი (STM32F030F4P6) აქვს 4 ასეთი ტაიმერი.

8. დააყენეთ პროექტი - დაამატეთ საჭირო ფაილები

ტაიმერის გამოსაყენებლად, ჩვენ უნდა შევიტანოთ stm32f10x_tim.c პერიფერიული ბიბლიოთეკის ფაილი. ანალოგიურად, დააწკაპუნეთ მარჯვენა ღილაკით Workspace-ზე (ფანჯარა მარცხნივ) StdPeriphLib ჯგუფზე, Add –> Add files, LibrariesSTM32F10x_StdPeriph_Driversrcstm32f10x_tim.c ფაილზე.

თქვენ ასევე უნდა ჩართოთ ამ ფაილის სათაურის გამოყენება. გახსენით stm32f10x_conf.h (დააწკაპუნეთ მაუსის მარჯვენა ღილაკით ამ ფაილის სახელზე კოდში, "გახსენით stm32f10x_conf.h". გააუქმეთ კომენტარი #include "stm32f10x_tim.h".

9. დაამატეთ ტაიმერი

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

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

ცოტა თეორია

ყველაფერი რაც ჩვენ გვჭირდება ტაიმერისგან არის დათვლა გარკვეულ მნიშვნელობამდე და წარმოქმნის შეფერხებას (დიახ, ჩვენ ასევე ვისწავლით შეფერხებების გამოყენებას). TIM6 ტაიმერი იმართება სისტემის ავტობუსიდან, მაგრამ არა პირდაპირ, არამედ პრესკალერის საშუალებით - მარტივი პროგრამირებადი კონტრ-გამყოფი (უბრალოდ იფიქრეთ, სსრკ-ში სპეციალური მიკროსქემები-მრიცხველები იწარმოებოდა, პროგრამირებადი კი განსაკუთრებული დეფიციტი იყო - და ახლა მე ასეთ მრიცხველზე ვსაუბრობ შემთხვევით). პრესკალერი შეიძლება დაყენდეს ნებისმიერ მნიშვნელობაზე 1-დან (ანუ ავტობუსის სრული სიხშირე, 24 MHz, მოხვდება მრიცხველზე) 65536-მდე (ანუ 366 Hz).

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

სინამდვილეში, პროცესი ცოტა უფრო რთულია: არსებობს ორი ARR რეგისტრი - გარე და შიდა. გაანგარიშებისას მიმდინარე მნიშვნელობა შედარებულია ზუსტად შიდა რეესტრთან და მხოლოდ გადასვლისას ხდება შიდა გარედან განახლება. ამრიგად, უსაფრთხოა ARR-ის შეცვლა, სანამ ტაიმერი მუშაობს - ნებისმიერ დროს.

Კოდი

კოდი ძალიან წააგავს წინას, რადგან. ყველა პერიფერიული მოწყობილობის ინიციალიზაცია ხდება იმავე გზით - ერთადერთი გამონაკლისით, რომ TIM6 ტაიმერი ჩამოკიდებულია APB1 ავტობუსზე. ამიტომ ტაიმერის ჩართვა: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

ახლა ვქმნით TIM_TimeBaseInitTypeDef ტიპის სტრუქტურას, ვაკეთებთ ინიციალიზაციას (TIM_TimeBaseStructInit), ვაყენებთ, გადავცემთ ტაიმერის ინიციალიზაციის ფუნქციას (TIM_TimeBaseInit) და ბოლოს ვააქტიურებთ ტაიმერის (TIM_Cmd).

TIM_TimeBaseInitTypeDef TIM_InitStructure; // სტრუქტურის დაყენება TIM_TimeBaseStructInit(&TIM_InitStructure); // სტრუქტურის ინიციალიზაცია TIM_InitStructure.TIM_Prescaler = 24000; // Prescaler TIM_InitStructure.TIM_Period = 1000; // ტაიმერის პერიოდი TIM_TimeBaseInit(TIM6, &TIM_InitStructure); // ტაიმერის დაყენების ფუნქცია TIM_Cmd(TIM6, ENABLE); // ჩართეთ ტაიმერი

რა არის ჯადოსნური ნომრები? როგორც გვახსოვს, ავტობუსში არის 24 MHz საათის სიხშირე (ჩვენი პროექტის პარამეტრებით). ტაიმერის პრესკალერის 24000-ზე დაყენებით ამ სიხშირეს ვყოფთ 24 ათასზე და მივიღებთ 1kHz. სწორედ ეს სიხშირე წავა ტაიმერის მრიცხველის შეყვანაში.

მრიცხველში მნიშვნელობა არის 1000. ეს ნიშნავს, რომ მრიცხველი გადაივლის 1000 ციკლში, ე.ი. ზუსტად 1 წამში.

ამის შემდეგ, ჩვენ ნამდვილად გვაქვს სამუშაო ტაიმერი. მაგრამ ეს ყველაფერი არ არის.

10. გაუმკლავდეთ შეფერხებებს

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

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

  1. საერთოდ ჩართეთ TIM6 ტაიმერის შეფერხებები;
  2. ჩართეთ ტაიმერის შეფერხება TIM6 მრიცხველის გადინებისთვის;
  3. შეფერხების დამმუშავებლის პროცედურის დაწერა;
  4. შეფერხების დამუშავების შემდეგ, გადატვირთეთ იგი.

შეფერხებების ჩართვა

მართალი გითხრათ, არაფერია რთული. უპირველეს ყოვლისა, ჩართეთ TIM6 შეფერხებები: NVIC_EnableIRQ(TIM6_DAC_IRQn); რატომ ასეთი სახელი? იმის გამო, რომ STM32 ბირთვში, TIM6-დან და DAC-დან შეფერხებებს ერთნაირი რიცხვი აქვთ. არ ვიცი, რატომ გაკეთდა ეს ასე - დანაზოგი, ნომრების ნაკლებობა, თუ უბრალოდ რაიმე მემკვიდრეობითი რამ - ნებისმიერ შემთხვევაში, ეს არ გამოიწვევს რაიმე პრობლემას, რადგან ეს პროექტი არ იყენებს DAC-ს. მაშინაც კი, თუ DAC გამოიყენებოდა ჩვენს პროექტში, ჩვენ შეგვიძლია გაგვერკვია, ვინ დარეკა კონკრეტულად შეფერხებაში შესვლისას. თითქმის ყველა სხვა ტაიმერს აქვს ერთი შეწყვეტა.

შეწყვეტის წყაროს მოვლენის კონფიგურაცია: TIM_ITConfig(TIM6, TIM_DIER_UIE, ENABLE); - ჩართეთ TIM6 ტაიმერის შეფერხება TIM_DIER_UIE ღონისძიებაზე, ე.ი. ARR მნიშვნელობის განახლების მოვლენა. როგორც ნახატიდან გვახსოვს, ეს ხდება იმავდროულად, როდესაც მრიცხველი ავსებს - ასე რომ, ეს არის ზუსტად ის მოვლენა, რომელიც ჩვენ გვჭირდება.

ამ დროისთვის, ტაიმერის შემთხვევების კოდი ასეთია:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); TIM_TimeBaseInitTypeDef TIM_InitStructure; TIM_TimeBaseStructInit(&TIM_InitStructure); TIM_InitStructure.TIM_Prescaler = 24000; TIM_InitStructure.TIM_Period = 1000; TIM_TimeBaseInit(TIM6, &TIM_InitStructure); TIM_Cmd(TIM6, ENABLE); NVIC_EnableIRQ(TIM6_DAC_IRQn); TIM_ITConfig(TIM6, TIM_DIER_UIE, ENABLE);

შეწყვეტა მართვა

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

ცოტა თეორია

მას უნდა ჰქონდეს ძალიან კონკრეტული სახელი, void TIM6_DAC_IRQHandler(void). ეს სახელი, ეგრეთ წოდებული შეფერხების ვექტორი, აღწერილია გაშვების ფაილში (ჩვენს პროექტში ეს არის startup_stm32f10x_md_vl.s - თავად ხედავთ, სტრიქონი 126). სინამდვილეში, ვექტორი არის შეფერხების დამმუშავებლის მისამართი და როდესაც ხდება შეფერხება, ARM ბირთვი გადადის საწყის არეალში (რომელზეც ითარგმნება გაშვების ფაილი - ანუ მისი მდებარეობა დაყენებულია სრულიად მკაცრად, თავიდანვე ფლეშ მეხსიერება), ეძებს ვექტორს იქ და მიდის კოდის სწორ ადგილას.

ღონისძიების შემოწმება

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

ჩვენს პროგრამაში ეს შემოწმება ასე გამოიყურება: თუ (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) - ყველაფერი ნათელია, TIM_GetITStatus ფუნქცია ამოწმებს, აქვს თუ არა ტაიმერს მითითებული მოვლენა და აბრუნებს 0 ან 1.

UIF დროშის გასუფთავება

მეორე ნაბიჯი არის შეფერხების დროშის გასუფთავება. დაბრუნება სურათზე: ბოლო UIF გრაფიკი არის შეწყვეტის დროშა. თუ ის არ არის გასუფთავებული, შემდეგი შეფერხების გამოძახება ვერ მოხერხდება და კონტროლერი კვლავ მოხვდება HARD_FAULT-ში (რა არის ეს!).

მოქმედებების შესრულება შეფერხებით

ჩვენ უბრალოდ გადავცვლით LED-ის მდგომარეობას, როგორც პირველ პროგრამაში. განსხვავება ისაა, რომ ახლა ჩვენი პროგრამა უფრო ართულებს! სინამდვილეში, ბევრად უფრო სწორია ასე დაწერა.

If(state) GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); სხვა GPIO_WriteBit (GPIOC, GPIO_Pin_8, Bit_RESET); სახელმწიფო = 1 - სახელმწიფო;

ჩვენ ვიყენებთ გლობალურ ცვლადს int state=0;

11. ყველა პროექტის კოდი ტაიმერით

#include "stm32f10x_conf.h" int state=0; void TIM6_DAC_IRQHandler(void) ( if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) (TIM_ClearITPendingBit(TIM6, TIM_IT_Update); if(state) GPIO_Write_Bit,PIOGt(GPIO_Write_Rit,PIOGt(GPIO_Write_Bit,PIOGt(BioCenter)GPIO_Write_Rite_PIOGt(GPIO_Write_Rite,PIOGt(BioClearITPendingBit) = 1 - state; ) ) void main() ( RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Speed ​​​​= GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_Init(GPIOC , &GPIO_InitStructure ); GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); TIM_TimeBaseInitTypeDef TIM_InitStructure; TIM_TimeBaseStructInit(&TIM_InitStructure); TIM_InitStructure.TIM_Prescaler = 24000; TIM_InitStructure.TIM_Period = 1000; TIM_TimeBaseInit(TIM6, &TIM_InitStructure); TIM_Cmd(TIM6 , ENABLE); NVIC_EnableIRQ(TIM6_DAC_IRQn); TIM_ITConfig(TIM6, TIM_DIER_UIE, ENABLE); while(1) ( ) )

დაარქივეთ ტაიმერის პროექტით.

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

მთელი ციკლი:

1. I/O პორტები

2. ტაიმერი და წყვეტები

3. ტაიმერის შედეგები

4. გარე შეფერხებები და NVIC

5. დააინსტალირეთ FreeRTOS

პოსტის ნახვები: 235