Professional Documents
Culture Documents
VDK_07
VDK_07
TIM_HandleTypeDef htim1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);
static void MX_ADC1_Init(void);
int main(void){
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_ADC1_Init();
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;
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
[Date] 3
THỰC HÀNH VI ĐIỀU KHIỀN
LCD_init();
LCD_clear();
HAL_TIM_Base_Start_IT(&htim1);
[Date] 4
THỰC HÀNH VI ĐIỀU KHIỀN
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);
[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);
}
}
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
[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);
[Date] 9
THỰC HÀNH VI ĐIỀU KHIỀN
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);}}
[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;
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
[Date] 14
THỰC HÀNH VI ĐIỀU KHIỀN
[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