Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 18

THỰC HÀNH VI ĐIỀU KHIỀN

HỌ VÀ TÊN : PHẠM VĂN KÝ TH. VĐK BÀI 7


MSSV : 22207053

Câu 1 : Hiển thị lên LCD 16*2


 Dòng 1 ( hoặc 8 ô đầu) : Giá trị đếm lên từ 0 với chu kỳ 1 giây
 Dòng 2 (hoặc 8 ô đầu) : Giá trị đếm số lần đầu nhấn nút SW1( sử dụng
ngắt )
#include "main.h"
#include "lcd.h"

TIM_HandleTypeDef htim1;

/* USER CODE BEGIN PV */


volatile uint8_t flag = 0;
volatile int time, phut=0;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);
static void MX_ADC1_Init(void);

/* USER CODE BEGIN 0 */


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN) {
if (GPIO_PIN == GPIO_PIN_1) flag=1;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
dem++;
}

int main(void){
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_ADC1_Init();

/* USER CODE BEGIN 2 */


LCD_init();
LCD_clear();
HAL_TIM_Base_Start_IT(&htim1);

while (1)
{
LCD_setCursor(0, 0);
LCD_printf("%d",dem);

[Date] 1
THỰC HÀNH VI ĐIỀU KHIỀN

LCD_setCursor(1, 0);
LCD_printf("%d",bam);
if (flag == 1) bam++;
}
/* USER CODE END 3 */
Giải thích chi tiết :
#include "main.h"
#include "lcd.h"

TIM_HandleTypeDef htim1;

/* USER CODE BEGIN PV */


volatile uint8_t flag = 0;
volatile int time, phut=0;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);
static void MX_ADC1_Init(void);

Đoạn mã này bắt đầu bằng việc bao gồm các thư viện cần thiết và khai báo biến
`htim1` kiểu `TIM_HandleTypeDef`.
Bao gồm thư viện và khai báo biến:
 main.h: Tập hợp các khai báo và định nghĩa cần thiết cho chương trình
chính.
 lcd.h: Thư viện dùng để điều khiển màn hình LCD.
 TIM_HandleTypeDef htim1;`: Biến này được khai báo để quản lý cấu
hình và trạng thái của timer 1.
Khai báo biến và hàm:
 volatile uint8_t flag = 0; : Biến flag được khai báo là một biến volatile kiểu
uint8_t (8-bit không dấu) để đánh dấu sự kiện nút được nhấn.
 volatile int time, phut=0; : Biến time và phut được khai báo để lưu trữ giờ
và phút. Biến time được khai báo mà không được khởi tạo, trong khi `phut`
được khởi tạo với giá trị ban đầu là 0.
 Các hàm SystemClock_Config ,MX_GPIO_Init, MX_TIM1_Init, và
`MX_ADC1_Init` được khai báo để cấu hình hệ thống, các cổng GPIO,
timer 1 và ADC 1 tương ứng.
Khởi tạo hàm

[Date] 2
THỰC HÀNH VI ĐIỀU KHIỀN

void SystemClock_Config(void); , static void MX_GPIO_Init(void); , static


void MX_TIM1_Init(void);, và static void MX_ADC1_Init(void);` là các hàm
khởi tạo để cấu hình các thành phần của hệ thống như clock, GPIO, timer và ADC.
Các hàm này sẽ được định nghĩa trong các file mã nguồn tương ứng
(`system_stm32xxxx.c`, `gpio.c`, `tim.c`, và `adc.c`) và sẽ được gọi từ hàm
`main()` để thiết lập môi trường hoạt động của hệ thống.
/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN) {
if (GPIO_PIN == GPIO_PIN_1) flag=1;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
dem++;
}
Các hàm HAL_GPIO_EXTI_Callback và HAL_TIM_PeriodElapsedCallback
là các hàm callback được định nghĩa trong thư viện HAL (Hardware Abstraction
Layer) của STM32Cube để xử lý các sự kiện ngắt ngoại vi (External Interrupt) và
sự kiện đếm chu kỳ của timer.
Hàm HAL_GPIO_EXTI_Callback :
 Được gọi khi có sự kiện ngắt ngoại vi trên các chân GPIO được cấu hình là
ngắt ngoại vi.Tham số GPIO_PIN là mã của chân GPIO gây ra sự kiện ngắt
ngoại vi.
 Trong hàm này, kiểm tra xem sự kiện ngắt ngoại vi có xảy ra trên chân
GPIO_PIN_1 hay không và nếu có, biến `flag` được đặt thành 1 để đánh
dấu sự kiện này đã xảy ra.
Hàm HAL_TIM_PeriodElapsedCallback :
 Được gọi khi timer đếm đạt đến giá trị chu kỳ.
 Tham số htim là con trỏ đến cấu trúc TIM_HandleTypeDef của timer đã
kích hoạt callback.Trong hàm này, biến `dem` được tăng lên mỗi khi
callback được gọi, thường được sử dụng để đếm các sự kiện xảy ra trong
một khoảng thời gian hoặc để thực hiện các tác vụ được kích hoạt theo chu
kỳ timer.
int main(void){
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_ADC1_Init();

/* USER CODE BEGIN 2 */

[Date] 3
THỰC HÀNH VI ĐIỀU KHIỀN

LCD_init();
LCD_clear();
HAL_TIM_Base_Start_IT(&htim1);

Khởi tạo hệ thống và cấu hình cần thiết:


 HAL_Init() : Khởi tạo HAL (Hardware Abstraction Layer), là lớp trừu
tượng phần cứng của STM32Cube HAL.
 SystemClock_Config(): Cấu hình clock hệ thống (System Clock). Hàm này
thường được định nghĩa trong file `system_stm32xxxx.c` để thiết lập clock
cho vi xử lý STM32 dựa trên cấu hình được xác định trong các file
`stm32xxxx_hal_conf.h` và `stm32xxxx_hal_conf_template.h`.
 MX_GPIO_Init() : Cấu hình các cổng GPIO. Hàm này được tự động tạo ra
bởi CubeMX dựa trên cấu hình của bạn trong phần môi trường làm việc của
CubeMX.
 MX_TIM1_Init() : Cấu hình timer 1. Tương tự, hàm này cũng được tự
động tạo ra bởi CubeMX dựa trên cấu hình của bạn.
 MX_ADC1_Init(): Cấu hình ADC 1. Tương tự như trên, hàm này cũng
được tự động tạo ra bởi CubeMX.
Khởi tạo màn hình LCD và khởi động timer với chế độ ngắt (interrupt):
 LCD_init() : Khởi tạo màn hình LCD. Có thể đây là một hàm do bạn tự
định nghĩa hoặc sử dụng một thư viện đã có sẵn.
 LCD_clear(): Xóa màn hình LCD để chuẩn bị hiển thị dữ liệu mới.
 HAL_TIM_Base_Start_IT(&htim1) : Khởi động timer 1 ở chế độ ngắt.
Điều này có nghĩa là timer sẽ tạo ra ngắt sau mỗi chu kỳ, và một hàm
callback (HAL_TIM_PeriodElapsedCallback) sẽ được gọi mỗi khi ngắt
xảy ra.
while (1)
{
LCD_setCursor(0, 0);
LCD_printf("%d",dem);
LCD_setCursor(1, 0);
LCD_printf("%d",bam);
if (flag == 1) bam++;
}
/* USER CODE END 3 */
Trong vòng lặp vô hạn `while(1)`, các hành động sau được thực hiện :

[Date] 4
THỰC HÀNH VI ĐIỀU KHIỀN

Hiển thị dữ liệu lên màn hình LCD


 LCD_setCursor(0, 0); : Di chuyển con trỏ đến vị trí (0, 0) trên màn hình
LCD, tức là hàng đầu tiên và cột đầu tiên.
 LCD_printf("%d", dem); : In giá trị của biến `dem` lên màn hình LCD ở
vị trí hiện tại của con trỏ. Giá trị này thường là số lần đếm được tăng lên mỗi
khi hàm callback `HAL_TIM_PeriodElapsedCallback` được gọi.
 LCD_setCursor(1, 0); : Di chuyển con trỏ đến vị trí (1, 0) trên màn hình
LCD, tức là hàng thứ hai và cột đầu tiên.
 LCD_printf("%d", bam); : In giá trị của biến `bam` lên màn hình LCD ở
vị trí hiện tại của con trỏ. Giá trị này thường là số lần đếm được tăng lên mỗi
khi biến `flag` được đặt thành 1
 Kiểm tra và tăng biến `bam` nếu `flag` bằng 1: if (flag == 1) bam++; :
Kiểm tra nếu biến `flag` có giá trị bằng 1. Nếu có, biến `bam` được tăng lên
một đơn vị.
Vòng lặp này sẽ lặp mãi mãi, hiển thị các giá trị `dem` và `bam` lên màn hình
LCD và tiếp tục kiểm tra biến `flag`. Nếu `flag` được đặt thành 1 (ngắt nút nhấn),
biến `bam` sẽ được tăng lên. Điều này cho phép cập nhật hiển thị trên màn hình
LCD theo thời gian và sự kiện.
Câu 2 : Thiết kế đồng hồ đếm giây hiển thị lên LCD 16*2 như sau :
 Lúc khởi chạy giá trị thời gian là 00:00 hiển thị LCD.
 Bấm nút SW1 thực hiện việc bắt đầu đếm và tạm dừng.
 Bấm nút SW2 để đặt lại giá trị hiển thị về 00:00
 Phải sử dụng ngắt ngoài cho SW1, SW2 và ngắt timer.
#include "main.h"
#include "lcd.h"

TIM_HandleTypeDef htim1;
volatile uint8_t flag = 0;
volatile int time, phut=0;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);
static void MX_ADC1_Init(void);

/* USER CODE BEGIN 0 */


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN) {
if (GPIO_PIN == GPIO_PIN_1){

[Date] 5
THỰC HÀNH VI ĐIỀU KHIỀN

if (flag == 1){
flag = 0;
} else flag = 1;
}
if (GPIO_PIN == GPIO_PIN_2) flag=2;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
if (flag==1) time++;
if (phut==60) phut=0;
if (time==60){
time=0;
phut++;
}
if (flag==2){
phut = 0;
time = 0;
}}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
LCD_init();
LCD_clear();
HAL_TIM_Base_Start_IT(&htim1);
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start_IT(&hadc1);
while (1)
{
LCD_setCursor(0, 0);
LCD_printf("%02d:%02d",phut,time);
}
}

Giải thích chi tiết :


#include "main.h"
#include "lcd.h"

TIM_HandleTypeDef htim1;
volatile uint8_t flag = 0;
volatile int time, phut=0;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);

[Date] 6
THỰC HÀNH VI ĐIỀU KHIỀN

static void MX_ADC1_Init(void);


 Giải thích giống trên câu 1 :

/* USER CODE BEGIN 0 */


void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN) {
if (GPIO_PIN == GPIO_PIN_1){
if (flag == 1){
flag = 0;
} else flag = 1;
}
if (GPIO_PIN == GPIO_PIN_2) flag=2;
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
if (flag==1) time++;
if (phut==60) phut=0;
if (time==60){
time=0;
phut++;
}
if (flag==2){
phut = 0;
time = 0;
Đoạn mã này xử lý hai sự kiện chính:
Sự kiện ngắt ngoại vi (External Interrupt) từ các chân GPIO:
 Trong hàm HAL_GPIO_EXTI_Callback, nếu xảy ra sự kiện ngắt từ chân
GPIO_PIN_1, chương trình sẽ kiểm tra trạng thái của biến `flag`.
 Nếu `flag` đang là 1, nghĩa là đang tính thời gian, khi đó biến `flag` sẽ được
đặt lại thành 0, ngưng tính thời gian.
 Nếu `flag` không phải là 1, nghĩa là không đang tính thời gian, khi đó biến
`flag` sẽ được đặt thành 1, bắt đầu tính thời gian.
 Nếu xảy ra sự kiện ngắt từ chân GPIO_PIN_2, biến `flag` sẽ được đặt thành
2, điều này có thể chỉ định một hành động khác được xử lý sau này.
Sự kiện callback từ timer :
Trong hàm `HAL_TIM_PeriodElapsedCallback`, nếu `flag` đang là 1, nghĩa là
đang tính thời gian, biến `time` (biến đếm thời gian) sẽ được tăng lên.
 Nếu `phut` đạt giá trị 60, nghĩa là đã đếm đủ 60 phút, biến `phut` sẽ được
đặt lại thành 0.

[Date] 7
THỰC HÀNH VI ĐIỀU KHIỀN

 Nếu `time` đạt giá trị 60, nghĩa là đã đếm đủ 60 giây, biến `time` sẽ được
đặt lại thành 0 và biến `phut` sẽ tăng lên một đơn vị.
 Nếu `flag` đang là 2, nghĩa là đang có sự kiện đặc biệt (như reset), biến
`phut` và `time` sẽ được đặt lại thành 0.

int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
LCD_init();
LCD_clear();
 Giải thích giống câu 1
HAL_TIM_Base_Start_IT(&htim1);
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start_IT(&hadc1);
HAL_TIM_Base_Start_IT(&htim1) :
 Hàm này được sử dụng để khởi động timer 1 (`htim1`) ở chế độ cơ bản
(basic mode) và kích hoạt chế độ ngắt (interrupt mode).
 Khi timer đếm đạt đến giá trị chu kỳ, nó sẽ tạo ra một ngắt (interrupt), và
hàm caback `HAL_TIM_PeriodElapsedCallback` sẽ được gọi.
HAL_ADCEx_Calibration_Start(&hadc1):
 Hàm này được sử dụng để thực hiện quá trình hiệu chuẩn (calibration) cho
ADC 1 (`hadc1`).
 Quá trình hiệu chuẩn sẽ điều chỉnh các tham số của ADC để đảm bảo độ
chính xác của kết quả đo lường.
HAL_ADC_Start_IT(&hadc1):
 Hàm này được sử dụng để kích hoạt ADC 1 (`hadc1`) ở chế độ kích hoạt
ngắt (interrupt mode).
 Khi quá trình chuyển đổi ADC hoàn thành, nó sẽ tạo ra một ngắt, và hàm
callback `HAL_ADC_ConvCpltCallback` sẽ được gọi để xử lý dữ liệu đã
chuyển đổi.
while (1)

[Date] 8
THỰC HÀNH VI ĐIỀU KHIỀN

{
LCD_setCursor(0, 0);
LCD_printf("%02d:%02d",phut,time);
}
Trong vòng lặp vô hạn `while(1)`, các hành động sau được thực hiện :
Hiển thị thời gian lên màn hình LCD:
 LCD_setCursor(0, 0);`: Di chuyển con trỏ đến vị trí (0, 0) trên màn hình
LCD, tức là hàng đầu tiên và cột đầu tiên.
 LCD_printf("%02d:%02d", phut, time);`: In thời gian lên màn hình LCD
ở vị trí hiện tại của con trỏ. Thời gian được in ở dạng "phút:giây", với giá trị
của biến `phut` và `time`. Sử dụng `%02d` để đảm bảo rằng số được in sẽ có
hai chữ số, và nếu số đó có một chữ số thì sẽ được điền vào bằng số 0 ở phía
trước.
Vòng lặp này sẽ lặp mãi mãi, liên tục cập nhật thời gian lên màn hình LCD theo
giá trị của biến `phut` và `time`, trong định dạng "phút:giây".
Câu 3 : Viết chương trình tính giá trị nhiệt độ trung bình sau 100 lần chuyển đổi
và hiện thị giá trị đó lên LCD dưới dạng số thập phân với phần thập phân có 2 chữ
số
#include "main.h"
#include "lcd.h"

ADC_HandleTypeDef hadc1;
volatile uint8_t flag = 0;
volatile float temp=0;
volatile uint8_t idx=0;
volatile uint16_t sum_temp=0;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);
static void MX_ADC1_Init(void);

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){


flag=1;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_ADC1_Init();

[Date] 9
THỰC HÀNH VI ĐIỀU KHIỀN

/* USER CODE BEGIN 2 */


LCD_init();
LCD_clear();
HAL_TIM_Base_Start_IT(&htim1);
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start_IT(&hadc1);
while (1)
{
if (flag == 1){
if (idx<100){

sum_temp+=(HAL_ADC_GetValue(&hadc1)/4096.0)*3.3*100.0;
idx++;
}
else{
temp=sum_temp/100.0;
LCD_setCursor(0, 0);
LCD_printf("temp=%.2f oC",temp);
idx=0;
sum_temp=0;
}
}
flag = 0;
HAL_ADC_Start_IT(&hadc1);}}

Giải thích chi tiết :


#include "main.h"
#include "lcd.h"
 Giải thích giống câu 1
ADC_HandleTypeDef hadc1;
volatile uint8_t flag = 0;
volatile float temp=0;
volatile uint8_t idx=0;
volatile uint16_t sum_temp=0;
ADC_HandleTypeDef hadc1; :
 Biến `hadc1` được khai báo kiểu `ADC_HandleTypeDef`, được sử dụng để
quản lý cấu hình và trạng thái của module ADC.
 Thường thì khi sử dụng STM32 và thư viện HAL, các biến kiểu
`ADC_HandleTypeDef` sẽ được sử dụng để tham chiếu đến các cài đặt và
trạng thái của các module ADC trên vi điều khiển.
volatile uint8_t flag = 0; :

[Date] 10
THỰC HÀNH VI ĐIỀU KHIỀN

 Biến `flag` được khai báo là một biến `volatile` kiểu `uint8_t` (8-bit không
dấu), có nhiệm vụ đánh dấu trạng thái hoặc sự kiện trong chương trình.
 Đặc tính `volatile` đảm bảo rằng trạng thái của biến này sẽ không bị tối ưu
hóa bởi trình biên dịch và sẽ được cập nhật ngay khi có sự thay đổi.
volatile float temp=0; :
 Biến `temp` được khai báo là một biến `volatile` kiểu `float`, dùng để lưu
trữ giá trị nhiệt độ.
 Biến `volatile` đảm bảo rằng trạng thái của biến này sẽ được cập nhật ngay
khi có sự thay đổi và không bị tối ưu hóa bởi trình biên dịch.
volatile uint8_t idx=0; :
 Biến `idx` được khai báo là một biến `volatile` kiểu `uint8_t`, dùng để lưu
trữ chỉ số hoặc vị trí trong một mảng hoặc chuỗi.
 Đặc tính `volatile` đảm bảo rằng trạng thái của biến này sẽ được cập nhật
ngay khi có sự thay đổi và không bị tối ưu hóa bởi trình biên dịch
volatile uint16_t sum_temp=0; :
 Biến `sum_temp` được khai báo là một biến `volatile` kiểu `uint16_t`, dùng
để tính tổng các giá trị nhiệt độ.
 Đặc tính `volatile` đảm bảo rằng trạng thái của biến này sẽ được cập nhật
ngay khi có sự thay đổi và không bị tối ưu hóa bởi trình biên dịch.
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);
static void MX_ADC1_Init(void);
 Giải thích giống câu 1
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
flag=1;
}
Hàm `HAL_ADC_ConvCpltCallback` là một hàm callback được gọi tự động khi
quá trình chuyển đổi ADC hoàn thành. Cụ thể, hàm này được gọi sau khi tất cả các
kênh ADC đã được chuyển đổi xong.
/* USER CODE BEGIN 2 */
LCD_init();
LCD_clear();
 Giải thích giống câu 1

[Date] 11
THỰC HÀNH VI ĐIỀU KHIỀN

HAL_TIM_Base_Start_IT(&htim1);
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start_IT(&hadc1);
HAL_TIM_Base_Start_IT(&htim1); :
 Hàm này được sử dụng để khởi động timer 1 (`htim1`) ở chế độ cơ bản và
kích hoạt chế độ ngắt.
 Timer 1 thường được sử dụng để tạo ra các ngắt chu kỳ thời gian định kỳ.
HAL_ADCEx_Calibration_Start(&hadc1); :
 Hàm này được sử dụng để bắt đầu quá trình hiệu chuẩn cho ADC 1
(`hadc1`).
 Quá trình hiệu chuẩn này điều chỉnh các tham số của ADC để đảm bảo độ
chính xác của các giá trị đo lường.
HAL_ADC_Start_IT(&hadc1); :
 Hàm này được sử dụng để khởi động quá trình chuyển đổi ADC 1 (`hadc1`)
ở chế độ kích hoạt ngắt.
 Khi quá trình chuyển đổi hoàn thành, ADC 1 sẽ tạo ra một ngắt, và hàm
callback `HAL_ADC_ConvCpltCallback` sẽ được gọi để xử lý dữ liệu đã
chuyển đổi.
while (1)
{
if (flag == 1){
if (idx<100){

sum_temp+=(HAL_ADC_GetValue(&hadc1)/4096.0)*3.3*100.0;
idx++;
}
else{
temp=sum_temp/100.0;
LCD_setCursor(0, 0);
LCD_printf("temp=%.2f oC",temp);
idx=0;
sum_temp=0;
}
}
flag = 0;
HAL_ADC_Start_IT(&hadc1);
}
}
Đoạn mã trên thực hiện các thao tác sau:

[Date] 12
THỰC HÀNH VI ĐIỀU KHIỀN

Trong vòng lặp vô hạn `while(1)`, chương trình kiểm tra biến `flag` để xác định
xem đã có dữ liệu từ ADC để xử lý hay không.
Nếu biến `flag` bằng 1 (đã có dữ liệu từ ADC):
 Chương trình kiểm tra biến `idx` để xem đã đọc đủ số lượng mẫu nhiệt độ
chưa. Nếu `idx` nhỏ hơn 100, chương trình sẽ thêm giá trị nhiệt độ mới vào
tổng `sum_temp`, sau đó tăng `idx` lên 1.
 Nếu `idx` đạt 100, tức là đã đọc đủ số lượng mẫu, chương trình tính trung
bình cộng của các mẫu nhiệt độ và lưu vào biến `temp`. Sau đó, nhiệt độ
trung bình này được hiển thị trên màn hình LCD với hai chữ số thập
phân, và biến `idx` và `sum_temp` được đặt lại về 0 để chuẩn bị cho việc
đọc mẫu tiếp theo.
 Sau khi xử lý xong dữ liệu từ ADC, biến `flag` được đặt lại thành 0 và quá
trình chuyển đổi ADC được bắt đầu lại để đọc mẫu nhiệt độ tiếp theo.
Điều này tạo ra một vòng lặp liên tục, trong đó chương trình đọc và xử lý dữ liệu
từ ADC để tính trung bình cộng của nhiệt độ từ 100 mẫu, và hiển thị kết quả trên
màn hình LCD.

Câu 4 : Tạo mảng 20 phần tử để lưu các giá trị nhiệt độ sau mỗi lần chuyển đổi.
Cứ sau mỗi 20 lần chuyển đổi, chọn ra 1 giá trị nhiệt độ lớn nhất trong mảng và
hiện thị lên LCD. Nếu giá trị đó lớn hơn một giá trị nhiệt độ thì nhấp nháy đèn
LED ( chân PB12) với chu kỳ 300ms.
#include "main.h"
#include "lcd.h"

ADC_HandleTypeDef hadc1;
TIM_HandleTypeDef htim1;

volatile uint8_t flag = 0;


volatile int time, phut=0,dem=0,bam=0;
volatile float temp=0;
volatile float adc_value[20],adc_max;
volatile uint8_t idx=0;
volatile uint16_t sum_temp=0;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);
static void MX_ADC1_Init(void);

[Date] 13
THỰC HÀNH VI ĐIỀU KHIỀN

/* USER CODE BEGIN 0 */


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
if (temp >= 19.0){
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_12);
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
flag=1;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
LCD_init();
LCD_clear();
HAL_TIM_Base_Start_IT(&htim1);
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start_IT(&hadc1);
adc_max=0;
while (1)
{
if (flag == 1){
for (int i = 0; i<20; i++){
adc_value[i]=HAL_ADC_GetValue(&hadc1);
if (adc_value[i]>adc_max) adc_max=adc_value[i];
}Thực hành Vi điều khiển
5
temp=(adc_max/4096.0)*3.3*100.0;
LCD_setCursor(0, 0);
LCD_printf("temp=%.2f oC",temp);
}
flag = 0;
HAL_ADC_Start_IT(&hadc1);
}

Giải thích chi tiết :


#include "main.h"
#include "lcd.h"
 Giải thích giống câu 1
ADC_HandleTypeDef hadc1;
TIM_HandleTypeDef htim1;
ADC_HandleTypeDef hadc1; :

[Date] 14
THỰC HÀNH VI ĐIỀU KHIỀN

 Biến `hadc1` được khai báo kiểu `ADC_HandleTypeDef`, dùng để quản lý


cấu hình và trạng thái của module ADC 1.
 Biến này thường được sử dụng để tham chiếu đến cấu hình và trạng thái của
module ADC 1 trên vi điều khiển STM32.
TIM_HandleTypeDef htim1; :
 Biến `htim1` được khai báo kiểu `TIM_HandleTypeDef`, dùng để quản lý
cấu hình và trạng thái của timer 1.
 Biến này thường được sử dụng để tham chiếu đến cấu hình và trạng thái của
timer 1 trên vi điều khiển STM32.
volatile uint8_t flag = 0;
volatile int time, phut=0,dem=0,bam=0;
volatile float temp=0;
volatile float adc_value[20],adc_max;
volatile uint8_t idx=0;
volatile uint16_t sum_temp=0;
Trong đoạn mã trên, có các khai báo biến như sau
volatile uint8_t flag = 0; :
 Biến `flag` là một biến kiểu `uint8_t` (8-bit không dấu), được khai báo với
từ khóa `volatile`.
 Biến này thường được sử dụng để đánh dấu trạng thái hoặc sự kiện trong
chương trình.
 Từ khóa `volatile` đảm bảo rằng giá trị của biến sẽ không bị tối ưu hóa bởi
trình biên dịch và sẽ được cập nhật ngay khi có sự thay đổi.
volatile int time, phut=0, dem=0, bam=0; :
 Các biến `time`, `phut`, `dem`, và `bam` là các biến kiểu `int` (số nguyên),
được khai báo với từ khóa `volatile`.
 Các biến này dùng để lưu trữ thời gian, số phút, và các giá trị đếm khác
trong chương trình.
 Từ khóa `volatile` đảm bảo rằng giá trị của các biến này sẽ không bị tối ưu
hóa bởi trình biên dịch và sẽ được cập nhật ngay khi có sự thay đổi.
volatile float temp = 0; :
 Biến `temp` là một biến kiểu `float` (số thực dấu động), được khai báo với từ
khóa `volatile`.
 Biến này dùng để lưu trữ giá trị nhiệt độ.

[Date] 15
THỰC HÀNH VI ĐIỀU KHIỀN

 Từ khóa `volatile` đảm bảo rằng giá trị của biến này sẽ không bị tối ưu hóa
bởi trình biên dịch và sẽ được cập nhật ngay khi có sự thay đổi
volatile float adc_value[20], adc_max; :
 Mảng `adc_value` có kích thước 20 phần tử và biến `adc_max` là một biến
kiểu `float`.
 Mảng này được sử dụng để lưu trữ các giá trị đo từ ADC.
 Biến `adc_max` dùng để lưu trữ giá trị lớn nhất trong mảng `adc_value`.
 Cả hai đều được khai báo với từ khóa `volatile` để đảm bảo rằng giá trị của
chúng sẽ không bị tối ưu hóa bởi trình biên dịch và sẽ được cập nhật ngay
khi có sự thay đổi.
volatile uint8_t idx = 0; :
 Biến `idx` là một biến kiểu `uint8_t` (8-bit không dấu), được khai báo với từ
khóa `volatile`.
 Biến này dùng để đánh dấu chỉ số hoặc vị trí trong mảng `adc_value`.
 Từ khóa `volatile` đảm bảo rằng giá trị của biến này sẽ không bị tối ưu hóa
bởi trình biên dịch và sẽ được cập nhật ngay khi có sự thay đổi.
volatile uint16_t sum_temp = 0; :
 Biến `sum_temp` là một biến kiểu `uint16_t` (16-bit không dấu), được khai
báo với từ khóa `volatile`.
 Biến này dùng để tính tổng các giá trị nhiệt độ.
 Từ khóa `volatile` đảm bảo rằng giá trị của biến này sẽ không bị tối ưu hóa
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);
static void MX_ADC1_Init(void);
 Giải thích giống câu 1
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
if (temp >= 19.0){
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_12);
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
flag=1;
}
Trong đoạn mã trên, có hai hàm callback:

[Date] 16
THỰC HÀNH VI ĐIỀU KHIỀN

HAL_TIM_PeriodElapsedCallback :
 Được gọi mỗi khi một chu kỳ của timer kết thúc.
 Trong trường hợp này, hàm được sử dụng để kiểm tra giá trị nhiệt độ
(`temp`). Nếu nhiệt độ đạt hoặc vượt quá 19.0 độ C, chương trình sẽ thay đổi
trạng thái của chân GPIOB_PIN_12 bằng cách chuyển đổi trạng thái của nó
từ HIGH sang LOW hoặc ngược lại bằng hàm `HAL_GPIO_TogglePin`.
 Điều này có thể được sử dụng để điều khiển một thiết bị ngoại vi, chẳng hạn
như một đèn LED, dựa trên giá trị của nhiệt độ đo được.
HAL_ADC_ConvCpltCallback :
 Được gọi khi quá trình chuyển đổi ADC hoàn thành.
 Trong trường hợp này, hàm được sử dụng để đánh dấu sự kiện quá trình
chuyển đổi ADC đã hoàn thành bằng cách đặt biến `flag` bằng 1.
 Sau khi quá trình chuyển đổi ADC hoàn thành, dữ liệu nhiệt độ có thể được
xử lý trong phần chương trình chính dựa trên sự kiện này.
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
LCD_init();
LCD_clear();
HAL_TIM_Base_Start_IT(&htim1);
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start_IT(&hadc1);
 Giải thích giống câu 1
adc_max=0;
 Được sử dụng để gán giá trị 0 cho biến adc_max. Điều này có thể được sử
dụng để thiết lập giá trị ban đầu của biến adc_max trước khi bắt đầu quá
trình thu thập dữ liệu từ ADC
while (1)
{
if (flag == 1){
for (int i = 0; i<20; i++){
adc_value[i]=HAL_ADC_GetValue(&hadc1);
if (adc_value[i]>adc_max) adc_max=adc_value[i];
}

[Date] 17
THỰC HÀNH VI ĐIỀU KHIỀN

temp=(adc_max/4096.0)*3.3*100.0;
LCD_setCursor(0, 0);
LCD_printf("temp=%.2f oC",temp);
}
flag = 0;
HAL_ADC_Start_IT(&hadc1);
}
Dòng code trên là một vòng lặp vô hạn (`while(1)`) trong đó chương trình liên tục
kiểm tra giá trị của biến `flag`. Khi `flag` được đặt thành 1, nó chỉ thực hiện các
hành động bên trong vòng lặp.
 Kiểm tra điều kiện `if (flag == 1)`. Nếu `flag` bằng 1, nghĩa là đã có dữ
liệu mới từ ADC, thì chương trình tiếp tục vào phần xử lý dữ liệu.
 Trong vòng lặp `for`, chương trình thu thập 20 mẫu từ ADC và cập nhật giá
trị lớn nhất (`adc_max`) từ các mẫu này. Vòng lặp này chạy từ `i=0` đến
`i<20`. Mỗi mẫu được lấy bằng cách gọi hàm
`HAL_ADC_GetValue(&hadc1)` và được lưu vào mảng `adc_value`. Nếu
giá trị ADC mới lớn hơn giá trị `adc_max` hiện tại, `adc_max` được cập
nhật bằng giá trị mới này.
 Sau khi thu thập đủ 20 mẫu, giá trị lớn nhất `adc_max` được sử dụng để tính
toán giá trị nhiệt độ tương ứng (`temp`). Công thức tính nhiệt độ được áp
dụng là `(adc_max/4096.0)*3.3*100.0`.
 Hiển thị giá trị nhiệt độ (`temp`) lên màn hình LCD. Đầu tiên, chương trình
đặt con trỏ hiển thị tại vị trí (0, 0) bằng hàm `LCD_setCursor(0, 0)`, sau đó
hiển thị giá trị nhiệt độ với định dạng hai chữ số thập phân (`%.2f oC`) bằng
hàm `LCD_printf`.Đặt lại giá trị của biến `flag` về 0 để chuẩn bị cho lần lấy
mẫu tiếp theo.
 Bắt đầu lại quá trình chuyển đổi ADC để lấy mẫu nhiệt độ tiếp theo bằng
cách gọi hàm `HAL_ADC_Start_IT(&hadc1)`.

[Date] 18

You might also like