Timer là một loại ngoại vi được tích hợp ở hầu hết các vi điều khiển, cung cấp cho người dùng nhiều ứng dụng như xác định chính xác một khoảng thời gian, đo – đếm xung đầu vào, điều khiển dạng sóng đầu ra, băm xung….
STM32F411 có 8 bộ Timer, trong đó có 1 bộ Advanced – control timer (TIM1) thường được các bộ thư viện sử dụng để tạo bộ đếm thời gian chuẩn của hệ thống (như ngắt System Tick, các hàm tạo Delay, TimeOut…), và 7 bộ General – purpose timer (TIM2 đến TIM5 và TIM9 đến TIM11).
Bài viết này giúp các bạn hiểu về cấu trúc, chức năng và thực hành một ứng dụng cơ bản của khối timer trên vi điều khiển STM32F411.
[HỌC ONLINE: LẬP TRÌNH VI ĐIỀU KHIỂN STM32, VI XỬ LÝ ARM CORTEX – M]
I. Time-base unit (Khối cơ sở của bộ Timer): Thành phần chính của timer chính là bộ đếm – counter (CNT), với các ngưỡng trên được thiết lập bởi thanh ghi Auto Reload (ARR). Counter có thể đếm lên lên hoặc đếm xuống. Clock đưa vào bộ đếm có thể được chia bởi một bộ chia tần – Prescaler.
Người dùng có thể thực hiện các lệnh đọc, ghi vào các thanh ghi CNT, ARR và PSC để cấu hình cho khối cơ sở của mỗi bộ Timer.
– Counter Register (TIMx_CNT): Khi hoạt động, thanh ghi này tăng hoặc giảm giá trị theo mỗi xung clock đầu vào. Tùy vào bộ timer mà counter này có thể là 16bit hoặc 32bit.
– Prescaler Register (TIMx_PSC): Giá trị của thanh ghi bộ chia tần (16bit) cho phép người dùng cấu hình chia tần số đầu vào (CK_PSC) cho bất kì giá trị nào từ [1- 65536]. Sử dụng kết hợp bộ chia tần của timer và của RCC giúp chúng ta có thể thay đổi được thời gian của mỗi lần CNT thực hiện đếm, giúp tạo ra được những khoảng thời gian, điều chế được độ rộng xung phù hợp với nhu cầu.
– Auto-Reload Register (TIMx_ARR): Giá trị của ARR được người dùng xác định sẵn khi cài đặt bộ timer, làm cơ sở cho CNT thực hiện nạp lại giá trị đếm mỗi khi tràn (overflow khi đếm lên – CNT vượt giá trị ARR, underflow khi đếm xuống – CNT bé hơn 0).Tùy vào bộ timer mà counter này có thể là 16bit hoặc 32bit.
II. Các chế độ hoạt động:
– Các chế độ đếm: Mỗi bộ timer đều hỗ trợ 3 chế chế độ đếm sau:
+ Upcounting mode (chế độ đếm lên): Ở chế độ này, CNT đếm lên từ 0 (hoặc một giá trị nào đó được người dùng ghi vào CNT trước) đến giá trị của thanh ghi ARR, sau đó CNT bắt đầu lại từ 0. Lúc này có sự kiện tràn counter – overflow, sự kiện này có thể tạo yêu cầu ngắt nếu người dùng cấu hình cho phép ngắt. Một ví dụ với ARR = 36:
+ Downcouting mode (chế độ đếm xuống): Ở chế độ này, CNT đếm xuống từ giá trị thanh ghi ARR (hoặc 1 giá trị nào đó do người dùng ghi trực tiếp vào CNT trước) đến 0, sau đó CNT bắt đầu lại từ giá trị ARR, lúc này có sự kiện tràn counter – underflow, sự kiện này có thể tạo yêu cầu ngắt nếu người dùng cấu hình cho phép ngắt. Một ví dụ với ARR = 36:
+ Center-Aligned mode (chế độ đếm lên và xuống): Ở chế độ này, counter sẽ đếm lên từ 0 (hoặc một giá trị nào đó được người dùng ghi vào CNT trước) đến giá trị thanh ghi ARR – 1, lúc này xuất hiện sự kiện tràn counter – overflow, tiếp theo CNT sẽ đếm xuống từ ARR tới 1, lúc này có sự kiện tràn counter – underflow, sau đó CNT sẽ về giá trị 0 và bắt đầu lại quá trình đếm lên. Một ví dụ với ARR = 06:
III. Lựa chọn Clock cho bộ Timer:
Counter clock là nguồn gốc của hoạt động tăng/ giảm giá trị CNT, clock này có thể được cấu hình lựa chọn từ các nguồn sau:
– Internal clock (CK_INT): Chọn nguồn clock từ clock của hệ thống, có thể là từ thạch anh dao động tần số cao bên ngoài (HSE) hay bộ giao động RC tần số cao được tích hợp sẵn bên trong STM(HSI), từ đấy qua các bộ chia tần của hệ thống clock để cấp cho ngoại vi timer (TIM2 đến TIM5 có clock CK_PSC đầu vào bằng clock của bus APB1, TIM9 đến TIM11 có clock Ck_PSC bằng clock bus APB2). CK_PSC là clock chưa qua bộ Prescaler của khối timer.
– External clock mode1: Nếu chọn mode này thì counter có thể đếm mỗi khi chân external input pin (TIx) xuất hiện sườn lên hoặc sườn xuống, người dùng cấu hình chọn sườn.
– External clock mode2: Nếu chọn mode này thì counter có thể đếm mỗi khi chân external trigger input (ETR) xuất hiện sườn lên hoặc sườn xuống. (Mode này chỉ có ở các bộ TIM2, TIM3, TIM4).
– Internal trigger inputs (ITRx): Mode này cho phép sử dụng một timer làm bộ prescaler cho một bộ timer khác.
IV. Các kênh Caputer/Campare:
Mỗi bộ timer có 4 kênh Capture/Compare độc lập, mỗi kênh này phối hợp Time-base unit có thể tạo ra các tính năng sau:
– Input caputer: Ở chế độ Input capture, thanh ghi CCR của kênh đầu vào tương ứng sẽ được sử dụng để lưu giá trị của CNT khi phát hiện sự thay đổi mức logic (sườn lên/ sườn xuống) như được cấu hình trước đó. Từ đó có thể biết được khoảng thời gian giữa 2 lần có sườn lên hoặc sườn xuống.
– Output compare: Chế độ này thường được sử dụng để điều khiển đầu ra của 1 I/O PIN khi timer đạt được một chu kỳ thời gian, cũng chính là khi giá trị CNT đếm tới giá trị bằng với giá trị thanh ghi capture/campare (đã được nạp sẵn). Người dùng có thể cài đặt I/O Pin tương ứng với các giá trị logic: mức 1, mức 0 hoặc đảo giá trị logic hiện tại. Đồng thời, cờ ngắt được bật lên và yêu cầu ngắt cũng được tạo ra nếu người dùng cấu hình cho phép ngắt.
– PWM generation: Tính năng điều chế độ rộng xung cho phép tạo ra xung với tần số được xác định bởi giá trị của thanh ghi ARR, và chu kỳ nhiệm vụ (Duty cycle) được xác định bởi giá trị thanh ghi CCR.
STM32F411 hỗ trợ 2 chế độ PWM như sau:
+ Mode1: Nếu sử dụng chế độ đếm lên thì ngõ ra sẽ ở mức logic 1 khi CNT <CCR và ngược lại, ở mức 0 nếu CNT>CCR. Nếu sử dụng chế độ đếm xuống, đầu ra sẽ ở mức 0 khi CNT > CCR và ngược lại, ở mức 1 khi CNT < CCR.
+ Mode2: Nếu sử dụng chế độ đếm lên thì ngõ ra sẽ ở mức logic 0 khi CNT <CCR và ngược lại, ở mức 1 nếu CNT>CCR. Nếu sử dụng chế độ đếm xuống, đầu ra sẽ ở mức 1 khi CNT > CCR và ngược lại, ở mức 0 khi CNT<CCR.
PWM được sử dụng trong rất nhiều ứng dụng như điều chỉnh điện áp đầu ra để thay đổi độ sáng đèn LED, điều khiển động cơ; điều chế bản tin bởi sóng mang, tạo âm thanh…
– One-pulse mode output: Chế độ này là một trường hợp cụ thể PWM, giúp tạo ra 1 xung với độ rộng xung có thể cấu hình được. Counter sẽ được dừng lại tự động khi tràn overflow/underflow.
V. Thực hành với timer (Đang cập nhật)
Thực hành 1: Thay đổi trạng thái đèn LED mỗi 1 giây, sử dụng time-base unit.
Bước 1: Kích chọn dòng STM32F411VETx và sau đó chọn “Start Project”.
Bước 2: Chọn Serial Wire để nạp code
Bước 3: Click chuột phải vào chân PD12 sau đó tích vào ô GPIO_Output
Bước 3: Click vào mục Timers -> chọn TIM2
Áp dụng công thức (1)
với Prescaler = 1599, Period = 9999 ta có thời gian xảy ra ngắt Timer = 1s
Bước 4: Click vào tab NVIC Setting, click chọn Enabled cho TIM2 Interrupt
Bước 5: Setting project và sinh code
Bước 6: Lập trình bên phần mềm Keil C
Ở file main.c tab Functions, các bạn tìm đến file stm32f4xx_hal_tim.c. Gọi hàm HAL_TIM_PeriodElapsedCallback như sau:
1 2 3 4 5 6 7 8 9 10 |
/* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM2) { HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12); } } /* USER CODE END 0 */ |
Tiếp theo ở hàm main(), các bạn gọi lệnh HAL_TIM_Base_Start_IT để bắt đầu ngắt TIM2:
1 2 3 4 |
/* USER CODE BEGIN 2 */ HAL_TIM_Base_Start_IT(&htim2); /* USER CODE END 2 */ |
Bước 7: Build and Run. Quan sát led PD12 sẽ thay đổi trạng thái 1s 1 lần
Thực hành 2: PWM điều khiển độ sáng.
Cấu hình System Core và GPIO vẫn thực hiện tương tự như phần hướng dẫn ở trên.
Bước 1: Cấu hình cài đặt các thông số cho tính năng PWM
Với prescaler = 3 và Counter Period = 199, áp dụng công thức (1) ở trên ta tạo ra xung với tần số PWM_Frequency = 20000 KHz
Bước 2: Nối led ngoài vào chân PA1 để điều khiển độ sáng led
Bước 3: Cấu hình project và sinh code
Bước 4: Chuyển qua phần mềm Keil C để bắt đầu code
Ở USER CODE BEGIN PV ta khai báo biến uint8_t pwm để set giá trị cho thanh ghi CCR (Capture Compare Register)
1 2 |
/* USER CODE BEGIN PV */ uint8_t pwm=0; |
Gọi hàm HAL_TIM_PWM_Start để bắt đầu băm xung tại chân PA1
1 2 |
/* USER CODE BEGIN 2 */ HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); |
Trong hàm while() ta thực hiện các bước cho Led sáng dần, đến khi Led đạt độ sáng lớn nhất thì 2 giây sau cho độ sáng của Led giảm dần. Đến khi Led tắt thì 2 giây sau lặp lại các bước như trên.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ for(pwm =0;pwm<200;pwm=pwm+10) { __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,pwm); HAL_Delay(100); } HAL_Delay(2000); for(pwm =200;pwm>0;pwm=pwm-10) { __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_2,pwm); HAL_Delay(100); } HAL_Delay(2000); } |
Bước 5: Build project và nhấn Load để chạy chương trình.
TAPIT ARM R&D