SlideShare une entreprise Scribd logo
1  sur  117
Télécharger pour lire hors ligne
ARDUINO CHO NGƯỜI MỚI BẮT ĐẦU
Quyển cơ bản
minht57 lab
Lời nói đầu
Vi điều khiển là một thiết bị có thể lập trình được nhằm thực hiện các tác vụ định
sẵn. Thường việc lập trình này dành cho các kỹ sư có trình độ kỹ thuật nhất định và
thường rất khó khăn đối với các bạn học sinh tiếp cận. Nhờ sự ra đời của nền tảng
Arduino để đơn giản hóa việc tiếp cận với vi điều dành cho một cá nhân chưa biết gì
về lập trình vi điều khiển mà vẫn có thể tạo ra hàng nghìn ứng dụng khác nhau.
Quyển sách “Arduino cho người mới bắt đầu” (quyển cơ bản) được thiết kế cho các
bạn chưa biết hoặc biết một ít về lập trình Arduino. Nội dung sách chủ yếu viết về
định hướng suy nghĩ liên quan đến lập trình vi điều khiển nhằm mục tiêu phát triển
lâu dài, tạo một nền tảng căn bản nhất cho các bạn làm quen với lập trình vi điều
khiển. Bên cạnh đó, cung cấp cho bạn một số mẹo, thông tin liên quan đến lập trình vi
điều khiển nói chung và Arduino nói riêng.
Quyển sách bao gồm 4 phần đi từ linh kiện điện tử cơ bản đến lập trình những module
quan trọng của Arduino.
Phần 1 (Linh kiện điện tử cơ bản) sẽ cho chúng ta một số kiến thức cơ bản về các loại
linh kiện điện tử thường dùng.
Phần 2 (Lập trình C cơ bản) sẽ cung cấp một số kiến thức cơ bản nhất về lập trình C hỗ
trợ cho lập trình cho vi điều khiển.
Phần 3 (Một số module ngoại vi quan trọng) sẽ cung cấp những kiến thức cốt lõi trong
lập trình vi điều khiển nói chung và Arduino nói riêng.
Phụ lục nhằm cung cấp thêm những thông tin, mạch điện bổ trợ.
minht57 lab
Hướng dẫn đọc sách
Bộ sách Arduino dành cho người mới bắt đầu gồm 3 quyển đi từ mức độ cơ bản đến
chuyên sâu bao gồm quyển cơ bản, quyển rất cơ bản và quyển không còn là cơ bản.
Bộ sách này sẽ cung cấp cho bạn một tư duy làm việc với một vi điều khiển hơn là chỉ
thực hành Arduino đơn thuần.
Bộ 3 quyển sách sẽ cung cấp cho bạn rất nhiều thông tin ở mức căn bản dưới dạng từ
khóa và tóm tắt vấn đề (vì nếu giải thích vấn đề rõ ràng, chuyên sâu thì sẽ rất lan man
và dài dòng). Nếu bạn quan tâm một vấn đề nào cụ thể thì cứ dựa vào những từ khóa
đã được nêu ra và tìm hiểu thêm trên internet hoặc sách vở.
Vì mục tiêu của quyển sách là hướng đến những bạn học lập trình Arduino định
hướng chuyên sâu nên sách sẽ không tập trung vào từng module cảm biến, thiết bị
chấp hành hay một dự án nào cụ thể. Mà cấu trúc sách đi theo hướng học một vi điều
khiển tổng quát mà Arduino chỉ là một ví dụ cụ thể.
Nếu bạn chưa có kiến thức về lập trình Arduino thì quyển cơ bản được khuyên là bạn
đọc theo thứ tự các chương từ đầu đến cuối. Nếu một vấn đề mà bạn đã biết, bạn cũng
được khuyên là đọc lướt qua để tránh bỏ sót những thông tin mà có thể bạn chưa biết
(vì không phải tất cả các thông tin quan trọng đều được in đậm hoặc nhấn mạnh).
Nếu bạn đã có kiến thức cơ bản về lập trình Arduino thì bạn có thể đọc một chương
bất kỳ mà bạn quan tâm. Nếu có thời gian thì bạn nên xem những chương mà bạn đã
biết để kiểm chứng cũng như xem liệu có kiến thức nào chưa biết hay không.
Nếu bạn cảm thấy văn phong hoặc cách tiếp cận của quyển sách không phù hợp với
bạn thì bạn có thể bỏ qua.
Trong quá trình viết và biên soạn sách không thể tránh khỏi những sai sót về mặt nội
dung cũng như hình thức. Nhóm tác giả rất mong nhận được sự góp ý của các bạn
đọc để quyển sách ngày càng hoàn thiện hơn và có thể truyền tải nội dung đến với
nhiều bạn đọc hơn.
Xin cảm ơn bạn đọc.
minht57 lab
Mục lục
Lời nói đầu ii
Hướng dẫn đọc sách iii
Mục lục iv
LINH KIỆN ĐIỆN TỬ CƠ BẢN 1
1 ĐIỆN TỬ CƠ BẢN 2
1.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Điện trở . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Tụ điện . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.4 Cuộn cảm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.5 Diode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.6 Transistor BJT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.7 MOSFET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.8 IC logic cần biết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.9 Op-amp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
LẬP TRÌNH C CƠ BẢN 12
2 LẬP TRÌNH C CƠ BẢN 13
2.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2 Các thành phần cơ bản trong ngôn ngữ C . . . . . . . . . . . . . . . . . 13
2.3 Các hệ đếm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.4 Biểu thức và phép toán . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.5 Một số hàm C thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . 16
Hàm if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Hàm switch ... case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Vòng lặp for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Vòng lặp while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.6 Hàm/Chương trình con . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.7 Mảng một chiều . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.8 Con trỏ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
MỘT SỐ MODULE NGOẠI VI QUAN TRỌNG 27
3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 28
3.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 29
pinMode() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
digitalWrite() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
digitalRead() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.3 Một số module mẫu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Đèn LED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Module Relay . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Nút nhấn (button) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4 TIME 37
4.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 37
delay() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
delayMicroseconds() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
millis() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
micros() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
4.3 Ghi chú . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5 UART 40
5.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Serial.begin() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Serial.end() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Serial.print()/Serial.println() . . . . . . . . . . . . . . . . . . . . . . . . . 41
Serial.write() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Serial.availableForWrite() . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Serial.available() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Serial.read() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Một số hàm khác . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Lưu ý . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.3 Một số module mẫu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Module bluetooth HC-05 . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Giao tiếp giữa 2 Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.4 Lời kết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6 ANALOG 51
6.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 52
analogRead() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
analogReference() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
analogWrite() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.3 Một số module mẫu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Biến trở . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Module điều khiển động cơ L298N . . . . . . . . . . . . . . . . . . . . . 56
6.4 Lời kết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
7 I2C 63
7.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
7.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Wire.begin() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Wire.requestFrom() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Wire.beginTransmission() . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Wire.endTransmission() . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Wire.write() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Wire.available() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Wire.read() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
Wire.setClock() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
Wire.onReceive() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Wire.onRequest() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
7.3 Một số module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Module IMU MPU6050 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Module thời gian thực RTC . . . . . . . . . . . . . . . . . . . . . . . . . 69
7.4 Lời kết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
8 SPI 73
8.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
8.2 Một số hàm thông dụng . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
SPISettings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
SPI.begin() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
SPI.end() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
SPI.beginTransaction() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
SPI.endTransaction() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
SPI.transfer(), SPI.transfer16() . . . . . . . . . . . . . . . . . . . . . . . . 76
SPI.usingInterrupt() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
8.3 Một số module thường dùng . . . . . . . . . . . . . . . . . . . . . . . . 76
Thẻ nhớ SD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
8.4 Lời kết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
9 NGẮT - INTERRUPT 81
9.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
9.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 81
interrupts() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
noInterrupts() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
attachInterrupt() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
detachInterrupt() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
9.3 Lời kết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
PHỤ LỤC 84
A MỘT SỐ MODULE THÔNG DỤNG KHÁC 85
A.1 Module đo khoảng cách SRF-05 . . . . . . . . . . . . . . . . . . . . . . . 85
A.2 Cảm biến độ ẩm đất . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
B MỘT SỐ HÀM KHÁC 89
B.1 Hàm I/O nâng cao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
B.2 Hàm liên quan đến toán . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
B.3 Hàm làm việc với chuỗi . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
C MỘT SỐ KHÁI NIỆM CĂN BẢN 91
C.1 Một số khái niệm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
C.2 Một số từ ngữ chuyên ngành . . . . . . . . . . . . . . . . . . . . . . . . 92
C.3 Một số kiến thức cần biết . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Dòng sink/source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Push-pull và Open drain IO . . . . . . . . . . . . . . . . . . . . . . . . . 93
D TỔNG QUAN VỀ NỀN TẢNG ARDUINO 94
D.1 Phần cứng . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
D.2 Phần mềm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Cài đặt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Giới thiệu về Arduino IDE . . . . . . . . . . . . . . . . . . . . . . . . . . 99
E PHỤ LỤC MẠCH ĐIỆN 102
E.1 Mạch số 1: Diode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
E.2 Mạch số 2: Zener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
E.3 Mạch số 3: NPN Amplifier . . . . . . . . . . . . . . . . . . . . . . . . . . 104
E.4 Mạch số 4: Dùng BJT để đóng tắt relay . . . . . . . . . . . . . . . . . . . 105
E.5 Mạch số 5: IC số . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
Lời kết 109
Thông tin bản quyền 110
LINH KIỆN ĐIỆN TỬ CƠ BẢN
ĐIỆN TỬ CƠ BẢN 1
1.1 Giới thiệu . . . . . . . . . . . . 2
1.2 Điện trở . . . . . . . . . . . . . 2
1.3 Tụ điện . . . . . . . . . . . . . . 5
1.4 Cuộn cảm . . . . . . . . . . . . 7
1.5 Diode . . . . . . . . . . . . . . . 8
1.6 Transistor BJT . . . . . . . . . 8
1.7 MOSFET . . . . . . . . . . . . . 9
1.8 IC logic cần biết . . . . . . . 10
1.9 Op-amp . . . . . . . . . . . . 11
1.1 Giới thiệu
Chương này sẽ cung cấp một số kiến thức cơ bản về một số linh kiện
điện tử. Các linh kiện này sẽ là những linh kiện thường gặp trong suốt
quá trình học điện tử.
1.2 Điện trở
Linh kiện điện trở1
1: Điện trở và linh kiện điện trở là khác
nhau về mặt khái niệm. Trong thực tế,
linh kiện điện trở luôn được gọi tắt là
điện trở.
là linh kiện dùng để cản trở dòng điện (lưu ý, điện
trở là đại lượng vật lý đặc trưng cho tính cản trở dòng điện của vật
liệu).
Công thức tính giá trị của điện trở:
𝑅 =
𝑈
𝐼
◮ U: hiệu điện thế giữa hai đầu vật dẫn điện (đơn vị: Volt (V))
◮ I: cường độ dòng điện đi qua vẫn dẫn (đơn vị: Ampere (A))
◮ R: điện trở của vật dẫn (đơn vị: Ohm (Ω))
Công dụng
◮ Như tên điện trở, đây là linh kiện dùng để cản trở dòng điện
nhưng thật ra điện trở có nhiều công dụng như giảm điện áp
giữa tải với nguồn (ví dụ muốn cắm LED 3V vào nguồn 5V thì
cần mắc thêm điện trở để giảm áp), làm điện áp tham chiếu
bằng mạch chia áp, tạo các mạch khuếch đại/vi phân/tích phân
(khi kết hợp với tụ điện, cuộn cảm, op-amp), mạch lọc thông
thấp/thông cao (kết hợp với tụ điện, cuộn cảm), điện trở trong
mạch đo dòng,.. .
◮ Điện trở là linh kiện chuyển năng lượng từ điện sang nhiệt (tức
là trong lúc hoạt động sẽ sinh ra nhiệt) và thường lượng nhiệt
này không có ích. Mỗi điện trở sẽ có một công suất tối đa nên
khi dùng cần phải kiểm tra xem công suất mà điện trở phải chịu
có vượt qua công suất mặc định của điện trở không.
Các loại điện trở thường gặp:
◮ Điện trở cắm (through hole resistor)
• Chúng ta đọc giá trị điện trở dựa vào bảng màu (resistor
color code)
• Mỗi điện trở cắm thông thường đều có vạch màu để xác
định giá trị điện trở, đồng thời sai số của điện trở đó. Nếu
là điện trở có 4 vạch màu thì 2 vạch đầu tiên là giá trị, vạch
thứ 3 (vạch kế cuối) là hệ số nhân, vạch thứ 4 (vạch cuối
1 ĐIỆN TỬ CƠ BẢN 3
Hình 1.1: Hình ảnh điện trở chân cắm.
Nguồn từ Wikimedia và Wikimedia.
cùng) là sai số. Nếu là điện trở có 5 vạch màu thì 3 vạch
điều tiên là giá trị, vạch thứ 4 (vạch kế cuối) là hệ số nhân,
vạch thứ 5 (vạch cuối cùng) là sai số.
• Ví dụ điện trở có 4 vòng màu lần lượt là nâu-đen-cam-vàng
kim (gold): nâu có giá trị là 1, đen có giá trị là 0 thì giá trị
2 vạch đầu tiên là 10; cam là vạch thứ 3, cam có giá trị là 3
nên hệ số nhân là 103; vạch sai số là vàng kim, vàng kim có
sai số là ±5%. Giá trị điện trở là 10𝑥103 ± 5% = 10𝑘Ω ± 5%.
◮ Một số mẹo
• Một số khó khăn khi đọc giá trị điện trở là không xác định
được màu của điện trở và vạch nào là vạch đầu tiên hay
cuối cùng. Cách đơn giản là bạn đọc nhiều thì sẽ có kinh
nghiệm đọc giá trị điện trở hoặc bạn xác định dựa vòng
màu cuối cùng (vì thường vòng màu cuối cùng thường có
màu đặc biệt như vàng kim (gold)/bạch kim (siliver)).
• Ngoài bảng màu trên thì vẫn còn bảng màu đầy đủ hơn 2
2: Vì hình ảnh đó có bản quyền nên
mình không sao chép vào tài liệu của
mình được
.
Có 4 loại điện trở là 3/4/5/6 vòng màu, nhưng thường gặp
nhất là điện trở 4 và 5 vòng màu. Ngoài ra, điện trở trên
thực tế cũng có nhiều sai số khác nhau (0.05, 0.1, 0.25, 0.5,
1, 2, 5, 10, 20) nhưng trên thị trường thường gặp loại điện
trở 1% và 5%.
• Thông thường, điện trở 4 vòng màu và thân hình màu vàng
nhạt là điện trở 5%; điện trở 5 vòng màu và thân mình màu
xanh dương nhạt thì là điện trở 1%.
• Vậy việc đọc điện trở có cần thiết khi xung quanh chúng ta
có quá nhiều thiết bị có thể đo được điện trở (như VOM/Multi-
meter)? Câu trả lời là tùy thuộc vào bạn. Khi bạn tự đọc
được giá trị điện trở thì có những lợi ích như sau:
* Bạn có thể đọc được giá trị điện trở ngay lập tức mà
không cần dụng cụ đo.
* Đôi lúc chúng ta không thể đo giá trị của điện trở khi
điện trở đã hàn trong mạch (vì điện trở sẽ mắc song
song với mạch điện thì giá trị bạn đo giữa 2 đầu điện
trở là giá trị của điện trở cần đo và giá trị điện trở của
hệ thống tại 2 điểm đó) mà chúng ta cần ra bảng màu
hoặc cách ly điện trở ra khỏi mạch điện.
• Điện trở cắm thông thường sẽ có công suất 1/8W, 1/4W,
1/2W, 1W, 2W và 3W.
• Điện trở sẽ có một số giá trị tiêu chuẩn phù hợp cho từng
sai số khác nhau. Chúng sẽ có tiêu chuẩn giá trị điện trở
(standard resistor value) được chia thành
Ví dụ điện trở 5% sẽ có giá trị sau 1.0,
1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.7,
3.0, 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8,
7.5, 8.2, 9.1. Giá trị điện trở 5% sẽ từ 10Ω
đến 22MΩ. Ví dụ với hệ số là 1.0 thì sẽ có
các giá trị 10Ω, 100Ω, 1kΩ, 10kΩ, 100kΩ,
1MΩ, 10MΩ và tương tự cho những giá
trị khác.
E3 (>20%, có 3
giá trị điện trở), E6 (20%, có 6 giá trị điện trở), E12 (10%, có
12 giá trị điện trở), E24 (5%, có 24 giá trị điện trở), E48 (2%,
1 ĐIỆN TỬ CƠ BẢN 4
Hình 1.2: Bảng màu giá trị điện trở.
Nguồn từ Wikimedia.
có 48 giá trị điện trở), E96 (1%, có 96 giá trị điện trở), E192
(0.5%, 0.25%, <, có 192 giá trị điện trở).
◮ Điện trở dán SMD (SMD resistor)
• Giá trị của điện trở dán sẽ được in lên trên bề mặt (nếu có
thể). Các cách in trên điện trở có thể sẽ như sau:
* 103 = 10𝑥103 = 10𝑘Ω
* 100 = 10𝑥100 = 10Ω
* 1001 = 100𝑥101 = 1𝑘Ω
* 0 ≈ 0Ω
* 5𝑅6 = 5.6Ω
* 𝑅382 = 0.382Ω
* 068 = 0.068Ω
* 38𝐶 = 24.3𝑘Ω (tra theo chuẩn EIA-96)
• Kích thước điện trở:
* 0201: 0.024 x 0.012 inch, 1/20W
* 0402: 0.04 x 0.02 inch, 1/16W
* 0603: 0.06 x 0.03 inch, 1/10W
* 0805: 0.08 x 0.05 inch, 1/8W
* 1206: 0.12 x 0.06 inch, 1/4W
* 1210: 0.12 x 0.10 inch, 1/2W
* 1812: 0.18 x 0.12 inch, 1W
* 2010: 0.20 x 0.10 inch, 3/4W
* 2512: 0.25 x 0.12 inch, 1W
1 ĐIỆN TỬ CƠ BẢN 5
Hình 1.3: Điện trở dán SMD. Nguồn từ
Wikimedia.
• Một số mẹo:
* Nếu thấy điện trở in 3 chữ số thì có thể là điện trở 5%,
nếu in 4 chữ số thì có thể là điện trở 1% (lưu ý không
phải lúc nào cũng đúng).
◮ Biến trở (potentiometer)
Hình 1.4: Biến trở các loại. Nguồn từ
Wikimedia và Wikimedia.
• Biến trở là linh kiện có thể thay đổi được giá trị. Giá trị tối
đa của điện trở thông thường được ghi trực tiếp lên điện
trở.
1.3 Tụ điện
Chúng ta có thể hiểu đơn giản tụ điện là một linh kiện có thể chứa
được điện tích. Mỗi tụ điện sẽ có bản tụ ở 2 cực (được ngăn cách nhau
bởi 1 lớp điện môi, mỗi loại lớp điện môi khác nhau sẽ có một loại tụ
khác nhau) và các bản tụ này dùng để chứa điện tích. Tụ điện ngăn
cản không cho dòng điện một chiều đi qua nhưng cho phép dòng điện
xoay chiều đi qua (khái niệm đầy đủ hơn tại Wikipedia).
Hình 1.5: Một số loại tụ điện. Nguồn từ
Wikimedia.
Một số thông số cần lưu ý khi làm việc với tụ:
◮ Điện dung C (đơn vị Farad - F) thể hiện khả năng tích điện của
tụ. Điện dung càng lớn thì tích điện càng nhiều.
1 ĐIỆN TỬ CƠ BẢN 6
◮ Điện áp làm việc tối đa U (đơn vị Volt - V) là điện áp rơi tối đa
lên tụ có thể chịu được. Nếu điện áp hoạt động lớn hơn điện áp
làm việc thì gây chập tụ và nổ. Khi chọn điện áp làm việc cho tụ
hãy chọn tối thiểu có giá trị bằng 1.3 điện áp làm việc (ví dụ điện
áp làm việc 12V thì nên chọn tụ có điện áp 12𝑉 ∗1.3 = 15.6 ≈ 16𝑉).
◮ Công thức tính điện lượng Q:
𝑄 = 𝐶𝑈
• Thể hiện lượng điện tích được tích tụ ở bề mặt của tụ điện.
• Đơn vị là (Coulomd - C).
◮ Nhiệt độ làm việc (đơn vị C/F) là ngưỡng tối đa/tối thiểu của
nhiệt độ mà tụ có thể làm việc được. Thông thường chúng ta
không quan tâm đến thông số này, nhưng nếu thiết bị có chứa tụ
làm việc trong các môi trường khắc nghiệt (lò hơi, phòng lạnh,
ngoài trời,...) thì hãy chú ý đến nhiệt độ hoạt động của môi
trường.
◮ Ngoài ra còn nhiều thông số khác nhưng tùy vào ứng dụng cụ
thể mà bạn nên tìm hiểu thêm như hệ số biến đổi điện dung theo
nhiệt độ/thời gian, dòng rò (leakage current), tần số làm việc,
sai số (tolerance), ESR (Equivalent Series Resistance).. .
Một số loại tụ phổ biến:
◮ Tụ hóa (Electrolytic Capacitors): là tụ điện phân cực. Điều lưu ý
khi sử dụng tụ là không được gắn ngược chiều âm dương của tụ
với nguồn (nếu ngược thì sẽ gây nổ).
Hình 1.6: Các loại tụ hóa (từ trái sang
phải): tụ hóa chân cắm (Radial lead Elec-
trolytic Capacitors), tụ tantan (SMD Tan-
talium Capacitor), tụ nhôm (Aluminum
Electrolytic Capacitor) . Nguồn từ Wiki-
media.
• Tụ hóa thường có giá trị lớn (có thể từ vài chục nF đến vài
F). Tụ có giá trị lớn được gọi là siêu tụ (supercapacitor).
• Cực của tụ hóa thường được đánh dấu trên tụ (tụ hóa chân cắm/tụ nhôm
sẽ đánh dấu cực âm, tụ tantalium sẽ đánh dấu cực dương).
Hình 1.7: Hình ảnh ký hiệu cực trên tụ
điện. Nguồn từ Wikimedia và Wikime-
dia.
◮ Tụ gốm (ceramic capacitor) là tụ điện không phân cực, thường
có giá trị nhỏ.
• Nếu là tụ gốm loại cắm thì thường giá trị sẽ in lên tụ (đơn
vị là pF). Nếu là tụ dán SMD thì giá trị tụ sẽ được ghi trên
reel (cách đóng gói tụ khi bán).
1 ĐIỆN TỬ CƠ BẢN 7
Hình 1.8: Tụ hóa. Nguồn từ Wikimedia
và Wikimedia.
• Các thông tin cụ thể về điện áp hoạt động, giá trị tụ, sai
số,... sẽ được ghi trên bao bì đóng gói của nhà sản xuất.
Nếu bạn có được một con tụ không có thông tin thì rất khó
để tìm được thông số của nó.
• Kích thước của tụ cũng khá đa dạng. Bạn tham khảo thêm
với từ khóa “EIA & Metric Standard Sizes” để biết thêm chi
tiết.
◮ Những loại tụ khác bạn có thể tìm hiểu tại đây.
◮ Chi tiết về đặc tính tụ, bạn đọc tham khảo chuỗi bài tại electronics-
tutorials
1.4 Cuộn cảm
Cuộn cảm là linh kiện có khả năng sinh ra từ trường khi có dòng điện
chạy qua. Cuộn cảm đặc trưng bằng độ tự cảm (từ dung) có đơn vị đo
là Henry (H). Cuộn cảm sẽ cho dòng điện một chiều đi qua nhưng sẽ
cản trở dòng điện xoay chiều.
Hình 1.9: Một số loại cuộn cảm. Nguồn
từ Wikimedia.
Ghi chú: thông thường thì tác giả ít khi thấy dùng cuộn cảm với các
ứng dụng cơ bản nên chúng ta sẽ không đi quá sâu ngoài khái niệm.
Nếu bạn đọc đang làm liên quan đến lọc nhiễu, làm mạch nguồn
buck/boost/..., mạch lọc tần số,... thì bạn nên đọc tài liệu chuyên
ngành cụ thể để hiểu thêm về đặc tính của cuộn cảm vì lý thuyết về
cuộn cảm nhìn chung là hơi khó so với mức căn bản.
1 ĐIỆN TỬ CƠ BẢN 8
1.5 Diode
Diode là linh kiện bán dẫn chỉ cho phép dòng điện đi theo một chiều
nhất định. Diode có 2 khối bán dẫn ghép với nhau là bán dẫn loại
P và bán dẫn loại N. Dòng điện dương sẽ chạy từ P (anode) sang N
(cathode).
Hình 1.10: Một số loại diode (từ trái
sang phải): diode thường, LED (Light
Emitting Diode), diode zener . Nguồn từ
Wikimedia.
Các loại bán dẫn thường thấy: diode chỉnh lưu, zener, LED (Light
Emitting Diode), Schottky Diode,... Ngoài ra vẫn còn nhiều loại diode
khác như Gunn Diode, PIN diode, step recovery diode, laser diode,
photo diode,. . .
Một số thông số cần quan tâm:
◮ Điện áp phân cực thuận là điện áp tối thiểu để dòng điện có
thể chạy qua diode (thường giá trị điện áp phân cực thuận từ
0.15V đến vài volt tùy vào từng loại diode, giá trị mà chúng ta
hay nghe là 0.7V. Giá trị này thông thường phụ thuộc vào loại
vật liệu được trộn vào để làm bán dẫn).
◮ Điện áp ngược cực đại là điện áp tối đa nếu mắc ngược cực của
diode vào nguồn mà không ảnh hưởng gì. Nếu lớn hơn điện
áp này thì diode sẽ bị đánh thủng (thường giá trị điện áp đánh
thủng từ vài trăm đến vài nghìn volt).
◮ Dòng điện tối đa mà diode có thể tải được.
◮ Tần số đóng ngắt tối đa mà diode có thể đáp ứng được. 3
3: Bạn sẽ quan tâm thông số này khi làm
việc với các mạch đóng ngắt có tần số (ví
dụ như mạch nguồn xung).
1.6 Transistor BJT
Transistor BJT là linh kiện bán dẫn chủ động thường được sử dụng
như một phần tử khuếch đại (amplify) hoặc khóa điện tử (switch elec-
tronic signals). Transistor là từ được kết hợp từ “transfer” và “resistor”
vì nó sẽ chuyển đổi một tín hiệu yếu từ mạch điện có trở kháng thấp
sang mạch điện có trở kháng cao (“The transistor is a semiconductor
device which transfers a weak signal from low resistance circuit to
high resistance circuit. The words trans mean transfer property and
istor mean resistance property offered to the junctions.”)
Transistor được ghép từ 3 lớp bán dẫn: N-P-N thì được gọi là NPN
transistor, P-N-P thì được gọi là PNP transistor. Transistor có 3 cực là
B (base), C (collector), E (emitter). Chiều mũi tên là chiều của dòng
điện.
Transistor sẽ có 3 vùng hoạt động lần lượt là vùng tắt, vùng khuếch
đại và vùng bão hòa. Tùy vào ứng dụng mà bạn cần cấu hình mạch
transistor phù hợp để transistor rơi vào đúng vùng hoạt động. Ví dụ
khi bạn cho transistor hoạt động ở chế độ khuếch đại thì phải phân
1 ĐIỆN TỬ CƠ BẢN 9
Hình 1.11: Transistor BJT. Nguồn từ
Wikimedia và Wikimedia.
cực làm sao để điểm hoạt động của transistor rơi vào vùng đấy. Nếu
bạn muốn transistor hoạt động như công tắc thì để transistor chuyển
đổi giữa vùng tắt và vùng bão hòa (bạn đọc thấy hứng thú thì tìm hiểu
thêm về lý thuyết transistor, mặc dù hay nhưng khó).
Ký hiệu như hình 1.12.
Hình 1.12: Ký hiệu BJT. Nguồn từ Wiki-
media.
Một số thông số cần lưu ý: điện áp phân cực thuận 𝑉𝐵𝐸 , điện áp tối
đa hoạt động, dòng diện tối đa, tần số hoạt động. Tùy vào ứng dụng
cụ thể mà bạn cần quan tâm đến những thông số nào (khi bạn tìm hiểu
một ứng dụng cụ thể thì thông thường các tài liệu sẽ đề cập những
thông số về transistor). Thông tin của transistor thì bạn nên tìm trên
datasheet của sản phẩm.
1.7 MOSFET
MOSFET là viết tắt của "Metal-Oxide Semiconductor Field-Effect Tran-
sistor", là transistor hiệu ứng trường.
Hình 1.13: Cấu tạo của MOSFET kênh
N. Nguồn từ Wikimedia.
MOSFET cũng có 2 loại là N-MOSFET và P-MOSFET. MOSFET có 3
cực được đặt tên là G (gate), S (source), D (drain).
Ký hiệu như hình 1.14.
1 ĐIỆN TỬ CƠ BẢN 10
Hình 1.14: Ký hiệu của MOSFET kênh N
(bên trái) và kênh P (bên phải). Nguồn
từ Wikimedia.
Ghi chú về sự khác nhau lớn nhất giữa transistor BJT và MOSFET:
BJT điều khiển bằng dòng (current-driven device) tại cực B, trong khi
đó MOSFET được điều khiển bằng áp (voltage-controlled device) được
đặt vào cực G.
1.8 IC logic cần biết
IC (viết tắt của Integrated Circuit) là một mạch điện thực hiện một
chức năng nào đó được đóng gói sẵn trong một con chip. IC có nhiều
loại khác nhau nhưng có thể chia thành 4 nhóm chính:
◮ Microcomputers là IC có chứa bộ vi xử lý.
◮ Memory là IC dùng để chứa dữ liệu.
◮ Standard logics ICs là IC để được thực hiện một chức năng cụ
thể (chúng ta đề cập loại IC này trong đề mục này).
◮ Custom Logic ICs là IC được thực hiện theo nhu cầu của người
dùng.
Thông tin thú vị: mạch điện là sự kết hợp của transistor, tụ điện, điện
trở, cuộn cảm,. . . Tất cả các thành phần này trong chip được làm bằng
semiconductor trên một tấm bán dẫn gọi là wafer.
IC logic thường có hai loại: TTL và CMOS. Cơ bản chức năng của các
ICs này là giống nhau nhưng sẽ khác nhau nhau về một số đặc tính
nhất định.
◮ TTL ICs (Transistor-transistor logic ICs) được chế tạo bằng BJT.
◮ CMOS ICs (Complementary metal oxide semiconductor ICs)
được chế tạo từ một cặp p/n – MOSFET với nhau.
◮ Nếu điện áp hoạt động của chip là 5V:
• TTL ICs:
* Mức tín hiệu đầu vào: thấp (low) là 0 – 0.8V, cao (high)
là 2 – 5V.
* Mức tín hiệu đầu ra: thấp (low) là 0 – 0.5V, cao (high)
là 2.7 – 5V.
• CMOS ICs:
* Mức tín hiệu đầu vào: thấp (low) là 0 – 1.5V, cao (high)
là 3.5 – 5V.
* Mức tín hiệu đầu ra: thấp (low) là 0 – 0.05V, cao (high)
là 4.95 – 5V.
Một dòng chip Standard logics ICs khá phổ biến là dòng 7400. Trên
thị trường, chúng ta thường thấy có 74HCxx và 74LSxx. Sự khác nhau
cơ bản: chip 74HCxx (CMOS – High speed) được chế tạo bằng công
1 ĐIỆN TỬ CƠ BẢN 11
nghệ CMOS, chip 74LSxx (Low power Shottky) được chế tạo bằng
công nghệ TTL. (Ngoài ra còn có nhiều loại khác, tìm hiểu tại đây).
Một số chip nên biết:
◮ 74x00: NAND 2 ngõ vào
◮ 74x01: NAND 2 ngõ vào có cực thu hở (open-collector).
◮ 74x02: NOR 2 ngõ vào
◮ 74x08: AND 2 ngõ vào
◮ 74x09: AND 2 ngõ vào có cực thu hở (open-collector)
◮ 74x14: cổng đảo (có Schmitt-trigger)
◮ 74x32: OR 2 ngõ vào
◮ 74x86: XOR 2 ngõ vào
◮ 74x595: thanh ghi dịch 8-bit, ngõ vào nối tiếp, ngõ ra song song
◮ 74x4511: BCD sang 7-segment (dùng với LED 7 đoạn)
◮ Ngoài ra còn rất nhiều loại khác, vui lòng đọc tại đây.
1.9 Op-amp
Op-amp (operational amplifier – mạch khuếch đại thuật toán) là một
mạch khuếch đại “DC-coupled” với hệ số khuếch đại cao, có đầu vào
vi sai và thông thường có đầu ra đơn.
Hình 1.15: Op-amp. Nguồn từ Wikime-
dia.
Ký hiệu:
Hình 1.16: Ký hiệu của op-amp. Nguồn
từ Wikimedia.
◮ V+: Đầu vào không đảo
◮ V-: Đầu vào đảo
◮ 𝑉𝑜𝑢𝑡 : Đầu ra
◮ 𝑉𝑆+: Nguồn cung cấp điện dương
◮ 𝑉𝑆−: Nguồn cung cấp điện âm
Một số ứng dụng: op-amp có rất nhiều ứng dụng trong thực tế vì
opamp có thể thực hiện nhiều phép toán đảo, cộng, trừ, nhân, chia,
tích phân, đạo hàm tín hiệu. Mình sẽ liệt kê một số ứng dụng cơ bản
mà bạn có thể gặp ở mức độ cơ bản:
◮ Mạch đệm đảo/không đảo.
◮ Mạch khuếch đại đảo/không đảo.
◮ Mạch khuếch đại vi sai (sẽ gặp trong ứng dụng cân sử dụng cảm
biến loadcell).
◮ Mạch so sánh điện áp.
LẬP TRÌNH C CƠ BẢN
LẬP TRÌNH C CƠ BẢN 2
2.1 Giới thiệu . . . . . . . . . . . 13
2.2 Các thành phần cơ bản trong
ngôn ngữ C . . . . . . . . . . . . 13
2.3 Các hệ đếm . . . . . . . . . . 14
2.4 Biểu thức và phép toán . . . 14
2.5 Một số hàm C thường dùng 16
Hàm if . . . . . . . . . . . . . 16
Hàm switch ... case . . . . . . 17
Vòng lặp for . . . . . . . . . . 20
Vòng lặp while . . . . . . . . 22
2.6 Hàm/Chương trình con . . . 23
2.7 Mảng một chiều . . . . . . . 24
2.8 Con trỏ . . . . . . . . . . . . . 25
2.1 Giới thiệu
C là ngôn ngữ lập trình cấu trúc được sử dụng rộng rãi trong lập trình
nhúng. Chương này sẽ giúp bạn đọc nắm được những kiến thức cơ
bản cần thiết phục vụ cho lập trình vi điều khiển.
2.2 Các thành phần cơ bản trong ngôn ngữ C
Từ khóa là từ có ý nghĩa xác định dùng để khai báo dữ liệu, viết
hàm,...
◮ Ví dụ: if, else, for, switch,...
Tên nhằm thể hiện rõ ý nghĩa của hằng, biến, mảng, con trỏ,... trong
chương trình.
Kiểu dữ liệu:
STT Kiểu dữ liệu Chiều dài Ngưỡng giá trị
1 unsigned char 1 byte 0 → 255
2 char 1 byte -128 → 127
3 unsigned int 2 bytes (hoặc 4 bytes) 0 → 65535 (hoặc 0 → (232 − 1))
4 int 2 bytes (hoặc 4 bytes) -32768 → 32768 (hoặc −231 → (231 − 1))
5 unsigned short 2 bytes 0 → 65535
6 short 2 bytes -32768 → 32768
7 unsigned long 8 bytes 0 → 264
8 long 8 bytes −263 → (263 − 1)
9 float 4 bytes 3.4 ∗ 10−38 → 3.4 ∗ 10−38
10 double 8 bytes 1.7 ∗ 10−308 → 1.7 ∗ 10308
Khai báo và khởi tạo:
◮ Khai báo (declaration)
int this_is_parameter;
Biến this_is_parameter được khai báo với kiểu dữ liệu int.
◮ Khởi tạo (initialization)
int this_is_parameter = 100;
Biến this_is_parameter được khai báo với kiểu dữ liệu int và
khởi tạo giá trị ban đầu là 100.
2 LẬP TRÌNH C CƠ BẢN 14
2.3 Các hệ đếm
Các hệ đếm cơ bản trong lập trình C: hệ nhị phân (hệ 2 – binary), hệ
bát phân (hệ 8 – octal), hệ thập phân (hệ 10 – decimal) và hệ thập lục
phân (hệ 16 – hexadecimal)
Decimal Hex Octal Binary
0 0 0 0
1 1 1 1
2 2 2 10
3 3 3 11
4 4 4 100
5 5 5 101
6 6 6 110
7 7 7 111
8 8 10 1000
9 9 11 1001
10 A 12 1010
11 B 13 1011
12 C 14 1100
13 D 15 1101
14 E 16 1110
15 F 17 1111
16 10 20 1 0000
32 20 40 10 0000
64 40 100 100 0000
128 80 200 1000 0000
256 100 400 1 0000 0000
512 200 1000 10 0000 0000
1024 400 2000 100 0000 0000
Hệ đếm là cách biểu diễn con số dưới các dạng khác nhau. Ví dụ số
mười được biểu diễn dưới dạng nhị phân là 10102, dưới dạng bát phân
là 128, dưới dạng thập phân là 1010 và dưới dạng thập lục phân là
𝐴16.
Trong cuộc sống thường ngày, chúng ta dùng hệ thập phân, còn vi
điều khiển thi dùng hệ nhị phân. Từ đó trong lập trình, chúng ta dùng
hệ thập phân khi chúng ta muốn người đọc hiểu nhanh chóng (human-
readable), dùng hệ nhị phân khi muốn hiểu vi điều khiển đang hiểu
như thế nào, thập lục phân khi muốn viết gọn một chuỗi số nhị phân.
2.4 Biểu thức và phép toán
Biểu thức là sự phối hợp của những toán tử và toán hạng.
a = b + 1
2 LẬP TRÌNH C CƠ BẢN 15
Giá trị b được cộng cho 1, sau đó kết quả được gán lại cho a. 5
5: Trong phép toán, b và 1 là toán hạng,
+ là toán tử.
index += 2;
Giá trị index được cộng cho 2, sau đó kết quả được gán lại cho
index (đây là cách viết gọn).
Các loại phép toán:
◮ Phép toán số học: cộng (+), trừ (-), nhân (*), chia (/), chia lấy dư
(%).
10 + 8 / 2 = 14
12 % 5 = 2
◮ Phép quan hệ: lớn hơn (>), lớn hơn hoặc bằng (>=), bé hơn (<),
bé hơn hoặc bằng (<=), bằng (==) và khác (!=).
10 > 2
→ có giá trị 1 (đúng)
20 == 30
→ có giá trị 0 (sai)
◮ Phép toán luận lý: phủ định (!), và (&&) và hoặc (||).
5 && (8 < 9)
→ có giá trị 1 (đúng)
1 || 0
→ có giá trị 1 (đúng)
1 && 0
→ có giá trị 0 (sai)
◮ Phép toán trên bit (bitwise): và (&), hoặc (|), xor (ˆ), dịch trái («),
dịch phải (»), đảo (∼).
bit x bit y x&y x|y x ˆ y x
0 0 0 0 0 1
0 1 0 1 1 1
1 0 0 1 1 0
1 1 1 1 0 0
210 >> 1 = 00102 >> 1 = 00012
210 << 1 = 00102 << 1 = 01002
◮ Các phép toán khác: phép gán (=), phép gán kết hợp (+=, -=, *=,
/=, %=, »=, «=, &=, |=, ˆ=), phép tăng giảm (++, - -).
◮ Độ ưu tiên của các phép toán6
6: Bạn đọc tham khảo tại dayn-
hauhoc.com.
:
2 LẬP TRÌNH C CƠ BẢN 16
Độ ưu tiên Các phép toán Trình tự
1 () [] -> Trái → Phải
2 ! ∼ & * ++ – Phải → Trái
3 * / % Trái → Phải
4 + - Trái → Phải
5 << >> Trái → Phải
6 < <= > >= Trái → Phải
7 == != Trái → Phải
8 & Trái → Phải
9 ^ Trái → Phải
10 | Trái → Phải
11 && Trái → Phải
12 || Trái → Phải
13 ? : Phải → Trái
14 = += -= *= /= %= <<= >>= &= ^= |= Phải → Trái
2.5 Một số hàm C thường dùng
Hàm rẽ nhánh có điều kiện (if ... else if ... else ....)
1 if (biểu thức luận lý 1) {
2 <khối lệnh 1>
3 }
4 else if (biểu thức luận lý 2) {
5 <khối lệnh 2>
6 }
7 ...
8 else if (biểu thức luận lý n) {
9 <khối lệnh n>
10 }
11 else {
12 <khối lệnh n + 1>
13 }
◮ Hàng 1..3: nếu (if) biểu thức luận lý 1 đúng thì thực hiện khối lệnh
1 và thoát khỏi cấu trúc if
◮ Hàng 4..6: ngược lại nếu (else if) biểu thức luận lý 2 đúng thì thực
hiện khối lệnh 2 và thoát khỏi cấu trúc if
◮ Hàng 8..10: ngược lại (else if) nếu biểu thức luận lý n đúng thì thực
hiện khối lệnh n và thoát khỏi cấu trúc if
◮ Hàng 11..13: ngược lại (else) thực hiện khối lệnh n + 1.
Lưu ý:
◮ Từ khóa if, else if, else phải viết thường.
◮ Biểu thức luận lý đúng khi kết quả khác 0 (≠ 0) và sai khi kết quả
bằng 0 (== 0).
Một số dạng rút gọn của cấu trúc if
2 LẬP TRÌNH C CƠ BẢN 17
1 if (biểu thức luận lý 1) {
2 <khối lệnh 1>
3 }
hoặc
1 if (biểu thức luận lý 1) {
2 <khối lệnh 1>
3 }
4 else {
5 <khối lệnh 2>
6 }
Ví dụ: Link: https://onlinegdb.com/Sy_
yoAEWP
1 #include <stdio.h>
2
3 int main()
4 {
5 int a;
6 printf("Please add a: ");
7 scanf("%d", &a);
8
9 if(a>0) {
10 printf("%d is greater than 0", a);
11 }
12 else if (a == 0) {
13 printf("%d is equal to 0", a);
14 }
15 else {
16 printf("%d is less than 0", a);
17 }
18
19 return 0;
20 }
Kết quả:
Please add a: 6
6 is greater than 0
Hàm rẽ nhánh có điều kiện (switch ... case ...)
1 switch (biểu thức) {
2 case giá trị 1:
3 <khối lệnh 1>
2 LẬP TRÌNH C CƠ BẢN 18
4 break;
5 case giá trị 2:
6 <khối lệnh 2>
7 break;
8 ...
9 case giá trị n:
10 <khối lệnh n>
11 break;
12 default:
13 <khối lệnh n+1>
14 }
◮ Hàng 1: trong trường hợp biểu thức có giá trị là
◮ Hàng 2..4: giá trị 1 (case) thì thực hiện khối lệnh 1 và thoát ra khỏi
cấu trúc switch (break),
◮ Hàng 5..7: hoặc giá trị 2 (case) thì thực hiện khối lệnh 2 và thoát ra
khỏi cấu trúc switch (break),
◮ Hàng 9..11: hoặc giá trị n (case) thì thực hiện khối lệnh n và thoát
ra khỏi cấu trúc switch (break),
◮ Hàng 12..13: ngược lại (default) thì thực hiện khối lệnh n+1.
Lưu ý:
◮ Từ khóa switch, case, break, default phải viết thường.
◮ Biểu thức phải có kết quả là giá trị nguyên.
Ví dụ: Link: https://onlinegdb.com/
ryB1TC4bw
1 #include <stdio.h>
2
3 int main()
4 {
5 int month = 0;
6 printf("Please input month: ");
7 scanf("%d", &month);
8
9 switch(month)
10 {
11 case 1:
12 printf("Januaryn");
13 break;
14 case 2:
15 printf("Februaryn");
16 break;
17 case 3:
18 printf("Marchn");
19 break;
20 case 4:
21 printf("Apriln");
22 break;
23 case 5:
24 printf("Mayn");
25 break;
26 case 6:
27 printf("Junen");
28 break;
29 case 7:
2 LẬP TRÌNH C CƠ BẢN 19
30 printf("Julyn");
31 break;
32 case 8:
33 printf("Augustn");
34 break;
35 case 9:
36 printf("Septembern");
37 break;
38 case 10:
39 printf("Octobern");
40 break;
41 case 11:
42 printf("Novembern");
43 break;
44 case 12:
45 printf("Decembern");
46 break;
47 default:
48 printf("Input is wrongn");
49 }
50
51 switch(month)
52 {
53 case 1:
54 case 2:
55 case 3:
56 printf("Month %d is in Q1n", month);
57 break;
58 case 4:
59 case 5:
60 case 6:
61 printf("Month %d is in Q2n", month);
62 break;
63 case 7:
64 case 8:
65 case 9:
66 printf("Month %d is in Q3n", month);
67 break;
68 case 10:
69 case 11:
70 case 12:
71 printf("Month %d is in Q4n", month);
72 break;
73 }
74
75 return 0;
76 }
Kết quả:
Please input month: 2
February
Month 2 is in Q1
2 LẬP TRÌNH C CƠ BẢN 20
Vòng lặp for
1 for (biểu thức 1; biểu thức 2; biểu thức 3) {
2 <khối lệnh>
3 }
◮ Hàng 1: đầu tiên sẽ thực hiện biểu thức 1,
◮ Hàng 1..2: kiểm tra biểu thức 2 nếu đúng thì thực hiện khối lệnh.
Sau đó thực hiện biểu thức 3.
◮ Hàng 1..2: kiểm tra biểu thức 2 nếu vẫn còn đúng thì thực hiện
khối lệnh. Sau đó thực hiện biểu thức 3.
◮ Hàng 1..2: kiểm tra biểu thức 2 nếu vẫn còn đúng thì thực hiện
khối lệnh. Sau đó thực hiện biểu thức 3.
◮ Hàng 1: Kiểm tra biểu thức 2 nếu sai thì thoát khỏi vòng for.
Lưu ý:
◮ Từ khóa for phải viết thường.
Một số từ khóa đi với vòng lặp:
◮ break: dùng để thoát vòng lặp gần nhất chứa nó.
◮ continue: dùng để tiếp tục vòng lặp gần nhất, bỏ qua các lâu lệnh
phía sau lệnh continue.
Ví dụ 1: Link: https://onlinegdb.com/
HJCwC0E-P
1 #include <stdio.h>
2
3 int main()
4 {
5 int idx = 0;
6 for(idx = 0; idx < 10; idx++) {
7 printf("Loop %dn", idx);
8 }
9 printf("---n");
10 for(idx = 0; idx < 10; idx++) {
11 printf("Loop %dr", idx);
12 }
13 return 0;
14 }
Kết quả:
Loop 0
Loop 1
Loop 2
Loop 3
Loop 4
Loop 5
Loop 6
Loop 7
Loop 8
Loop 9
2 LẬP TRÌNH C CƠ BẢN 21
---
Loop 9
Ví dụ 2: Link: https://onlinegdb.com/
B19tyyBZD
1 #include <stdio.h>
2
3 int main()
4 {
5 int break_point = -1;
6 printf("Input a point [0..9]: ");
7 scanf("%d", &break_point);
8 if ((break_point > 9) || (break_point < 0)) {
9 printf("Input is wrong");
10 return 0;
11 }
12
13 int idx = 0;
14 for(idx = 0; idx < 10; idx++) {
15 if(idx == break_point) {
16 break;
17 }
18 printf("Loop %dn", idx);
19 }
20 printf("---n");
21 for(idx = 0; idx < 10; idx++) {
22 if(idx == break_point) {
23 continue;
24 }
25 printf("Loop %dn", idx);
26 }
27 return 0;
28 }
Kết quả:
Input a point [0..9]: 5
Loop 0
Loop 1
Loop 2
Loop 3
Loop 4
---
Loop 0
Loop 1
Loop 2
Loop 3
Loop 4
Loop 6
Loop 7
Loop 8
Loop 9
2 LẬP TRÌNH C CƠ BẢN 22
Vòng lặp while
1 while(điều kiện){
2 <khối lệnh>;
3 }
◮ Hàng 1..2: Nếu điều còn đúng thì thực hiện khối lệnh, nếu điều
kiện sai thì thoát ra khỏi vòng lặp.
Ví dụ: Link: https://onlinegdb.com/
SyByxkH-v
1 #include <stdio.h>
2
3 int main()
4 {
5 int idx = 10;
6 while(idx--) {
7 printf("Loop %dn", idx);
8 }
9 printf("---n");
10 idx = 10;
11 while(--idx) {
12 printf("Loop %dn", idx);
13 }
14
15 return 0;
16 }
Kết quả:
Loop 9
Loop 8
Loop 7
Loop 6
Loop 5
Loop 4
Loop 3
Loop 2
Loop 1
Loop 0
---
Loop 9
Loop 8
Loop 7
Loop 6
Loop 5
Loop 4
Loop 3
Loop 2
Loop 1
2 LẬP TRÌNH C CƠ BẢN 23
Thông tin
Trong vi điều khiển luôn cần một vòng lập vô tận (while(1), while(true),
for(;;)) để vi điều khiện lặp đi lặp lại một công việc nào đó chúng ta
lập trình (thật ra để vi điều khiển luôn được kiểm soát nằm trong
hoạt động của chương trình).
Nếu chúng ta lập trình trên vi điều khiển mà không liên quan đến
hệ điều hành (ví dụ RTOS) thì chỉ cần duy nhất một vòng lập vô
tận duy nhất vì khi vào vòng lặp thì không bao giờ thoát ra được.
Khi chúng ta sử dụng các vòng lập for, while, do...while thì cần phải
kiểm soát số lần lặp để tránh trường hợp rơi vào vòng lập vô tận.
Lưu ý về vòng lặp vô tận:
◮ Vòng lặp vô tận
1 unsigned char index = 100;
2 for(index = 100; index >= 0; index--) {
3 ...
4 }
◮ Vòng lặp được thực hiện 101 lần
1 char index = 100;
2 for(index = 100; index >= 0; index--) {
3 ...
4 }
2.6 Hàm/Chương trình con
Hàm được dùng để thực hiện một công việc nào đó cụ thể mà người
dùng muốn đặt ra. Người lập trình thường viết chương trình lớn thành
nhiều chương trình con để cho chương trình đỡ phức tạp và dễ dàng
trong đọc hiểu, bảo trì code. Chương trình con có thể gọi một chương
trình con khác. Nếu chương trình con gọi chính nó thì gọi là phương
pháp đệ quy. Lưu ý khi lập trình đệ quy là phải có điều kiện thoát,
nếu không chương trình sẽ lập vô tận và sinh ra các kết quả không
mong muốn.
Ví dụ 1: Link: https://onlinegdb.com/
rkLte1BZw
1 #include <stdio.h>
2
3 int sum(int a, int b)
4 {
5 return (a+b);
6 }
7
8 int main()
9 {
2 LẬP TRÌNH C CƠ BẢN 24
10 int result = sum(2,4);
11 printf("2 + 4 = %d", result);
12
13 return 0;
14 }
Kết quả:
2 + 4 = 6
Ví dụ 2: Hàng 3 được gọi là prototype function.
Prototype function bắt buộc khai báo khi
chương trình con được định nghĩa sau
hàm main.
1 #include <stdio.h>
2
3 int sum(int a, int b);
4
5 int main()
6 {
7 int result = sum(2,4);
8 printf("2 + 4 = %d", result);
9
10 return 0;
11 }
12
13 int sum(int a, int b)
14 {
15 return (a+b);
16 }
Kết quả trả về giống như trên.
2.7 Mảng một chiều
Mảng là tập hợp nhiều phần tử có chung kiểu dữ liệu. Chỉ số của các
phần tử trong mảng luôn bắt đầu là 0 và tăng dần 1 đơn vị cho các
phần tử tiếp theo.
Ví dụ: tạo ra mảng 10 phần tử có địa chỉ liên tiếp nhau có chung kiểu
dữ liệu là int.
int array[10];
Để tham chiếu phần tử trong mảng, ta thực hiện cú pháp sau:
int value = array[2];
Lưu ý
Khi khai báo mảng cần phải xác định số lượng phần tử trong mảng
nhằm tránh gây ra các sai sót về bộ nhớ.
2 LẬP TRÌNH C CƠ BẢN 25
Ví dụ:
1 #include <stdio.h>
2
3 #define NUMBER_OF_ARRAY 10
4
5 int main()
6 {
7 int array[NUMBER_OF_ARRAY];
8 int idx = 0;
9 for(idx = 0; idx < NUMBER_OF_ARRAY; idx++)
10 {
11 array[idx] = 2 * idx;
12 }
13
14 printf("Value at index 2: %dn", array[2]);
15
16 array[2] *= 3;
17
18 printf("New value at index 2: %dn", array[2]);
19
20 return 0;
21 }
Kết quả:
Value at index 2: 4
New value at index 2: 12
2.8 Con trỏ
Con trỏ sẽ chứa địa chỉ dùng để truy cập các biến/hàm thông qua địa
chỉ này. Ví dụ:
◮ int par; → Khai báo biến par có kiểu dữ liệu là int.
◮ int * px; → Khai báo con trỏ sẽ trỏ đến ô nhớ có kiểu dữ liệu là
int.
◮ px = &par; → Lấy địa chỉ của biến par gán cho con trỏ px.
◮ (* px) = 10; → Gán 10 vào ô nhớ được chứa trong con trỏ px (tức
là gán giá trị cho par thông qua con trỏ px).
Link: https://onlinegdb.com/
BkdMXyrWD
1 #include <stdio.h>
2
3 int main()
4 {
5 int parameter = 100;
6
7 printf("Value of parameter: %dn", parameter);
8 printf("Value of address of parameter: 0x%xn", &parameter);
9
2 LẬP TRÌNH C CƠ BẢN 26
10 int *px = &parameter;
11 printf("px is pointer. Value of px: 0x%xn", px);
12 printf("Value at address in pointer: %dn", (*px));
13
14 (*px) = 10;
15 printf("New value at address in pointer: %dn", (*px));
16 printf("Value of parameter: %dn", parameter);
17
18 return 0;
19 }
Kết quả (lưu ý địa chỉ của biến parameter có thể không giống bên
dưới):
Value of parameter: 100
Value of address of parameter: 0x39d73204
px is pointer. Value of px: 0x39d73204
Value at address in pointer: 100
New value at address in pointer: 10
Value of parameter: 10
MỘT SỐ MODULE NGOẠI VI QUAN
TRỌNG
GENERAL PURPOSE
INPUT/OUTPUT – GPIO 3
3.1 Giới thiệu . . . . . . . . . . . 28
3.2 Một số hàm thường dùng . 29
pinMode() . . . . . . . . . . . 29
digitalWrite() . . . . . . . . . 29
digitalRead() . . . . . . . . . 30
3.3 Một số module mẫu . . . . . 30
Đèn LED . . . . . . . . . . . . 30
Module Relay . . . . . . . . . 32
Nút nhấn (button) . . . . . . 33
3.1 Giới thiệu
General Purpose Input/Output (GPIO) là module dùng để đọc tín
hiệu vào hay xuất tín hiệu ra một hoặc một số chân vi điều khiển.
GPIO chỉ làm việc với 2 mức điện áp (điện áp thấp và điện áp cao)
tương ứng với mức logic 1 (mức điện áp cao – tùy thuộc vào vi điều
khiển: có thể là 1.8V, 3.3V, 5V,...) và mức logic 0 (mức điện áp thấp –
thường là 0V).
Xuất tín hiệu logic ra ngoài (output) là đưa điện áp mức cao (mức
logic 1) hay điện áp mức thấp (mức logic 0). Đọc tín hiệu điện áp vào
có kết quả là mức logic 0/1 (input) nhằm để xác định xem điện áp tại
chân vi điều khiển tương ứng là mức thấp hay cao.
Một số đặc tính khi chân của vi điều khiển được cấu hình là INPUT:
cấu hình mặc định của các chân Arduino (Atmega) là input, vì thế
chúng ta không cần khai báo khi hàm pinMode() khi sử dụng chúng
như một chân input. Chân vi điều khiển được cấu hình bằng cách
này sẽ ở trạng thái trở kháng cao (high-impedance state) – khoảng
100MΩ.
Thêm trở kéo lên/xuống cho một chân của vi điều khiển khi được
cấu hình là INPUT: một điều như không bắt buộc nhưng được xem
là thông lệ (nên được xem là bắt buộc) là phải xác định trạng thái của
chân (được cấu hình là input) nếu không có tín hiệu input từ ngoài
vào. Việc này được thực hiện bằng cách thêm một điện trở kéo lên (lên
mức điện áp cao - +5V đối với Arduino Atmega) hoặc điện trở kéo
xuống (xuống đất – 0V). Giá trị điện trở này thường được dùng là 10
kΩ.
Đặc tính của chân của vi điều khiển khi được cấu hình là INPUT_-
PULLUP: bên trong vi điều khiển Atmega đã có một điện trở 20K
được kéo lên nguồn có thể truy cập bằng phần mềm. Để truy cập được
điện trở này thì ta khai báo pinMode() với biến là INPUT_PULLUP.
Đặc tính của chân của vi điều khiển khi được cấu hình là OUTPUT:
khi được cấu hình là OUTPUT thì chân này ở trạng thái trở kháng
thấp (low-impedance state). Điều này có nghĩa là vi điều khiển có
thể cung cấp một lượng dòng điện lớn (vài milliampere đến vài chục
milliampere) ra ngoài. Các chân của vi điều khiển Atmega có thể dùng
mạch kiểu source (cung cấp dòng điện dương) hoặc kiểu sink (cung
cấp dòng điện âm) lên đến 40mA. Với lượng dòng điện này có thể đủ
làm sáng LED (phải mắc nối tiếp điện trở để hạn dòng) hoặc một vài
cảm biến nhưng không đủ dòng để bật tắt relay (rờ-le), motor.
Lưu ý: việc ngắn mạch hoặc để các thiết bị tiêu thụ dòng cao trên chân
của Arduino có thể hỏng một chân hoặc cả con vi điều khiển. Vì vậy,
3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 29
khi kết nối bất cứ thiết bị nào vào vi điều khiển phải xem xét đến dòng
điện tiêu thụ.
Ví dụ: dòng điện tiêu thụ của đèn LED khoảng 20mA, dòng tối đa mà
một chân vi điều khiển chịu được dòng điện tối đa là 40mA.Một đèn
LED có thể nối trực tiếp với chân vi điều khiển, nhưng không nên mắc
song song từ 2 LED trở lên vì sẽ gây hại đến vi điều khiển (có thể chết
chân hoặc chết cả vi điều khiển). Giải pháp là hãy dùng mạch đệm
dòng hoặc dùng BJT/FET để điều khiển.
3.2 Một số hàm thường dùng
pinMode()
Chức năng: dùng để cấu hình một chân là input hay output.
Cú pháp:
pinMode(pin, mode);
◮ pin: chân ta muốn cấu hình
◮ mode: INPUT, OUTPUT hoặc INPUT_PULLUP
Kết quả trả về: không.
Ví dụ: thiết lập chân 13 là ouput.
pinMode(13, OUTPUT); // sets the digital pin 13 as output
Lưu ý: các chân analog vẫn có thể sử dụng như digital (input/out-
put).
digitalWrite()
Chức năng: ghi mức logic HIGH (cao) hoặc LOW (thấp) ra chân được
chỉ định.
Cú pháp:
digitalWrite(pin, value);
◮ pin: chân ta muốn ghi mức giá trị.
◮ value: HIGH hoặc LOW.
Kết quả trả về: không.
Lưu ý:
3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 30
◮ Nếu không thiết lập pinMode() là OUTPUT và kết nối đèn LED
vào pin này, khi gọi hàm digitalWrite(HIGH) thì đèn LED có thể sẽ
sáng mờ. Nguyên nhân là ta không thiết lập pinMode là OUTPUT
thì mặc định là INPUT và gọi hàm digitalWrite(HIGH) sẽ bật điện
trở nội kéo lên nên sẽ xem như đèn LED mắc nối tiếp với trở hạn
dòng có giá trị lớn.
◮ Các chân analog vẫn có thể sử dụng như chân digital (ngoại trừ
chân A6, A7 của board Arduino Mini, Arduino Nano, Pro Mini).
digitalRead()
Chức năng: đọc giá trị logic từ chân được chỉ định.
Cú pháp:
digitalRead(pin);
◮ pin: chân mà ta muốn đọc
Giá trị trả về: HIGH hoặc LOW.
Lưu ý:
◮ Nếu chân được đọc tín hiệu mà không được nối vào đâu thì giá
trị trả về có thể là HIGH hoặc LOW (và có thể thay đổi đột ngột).
◮ Các chân analog vẫn có thể sử dụng như chân digital (ngoại trừ
chân A6, A7 của board Arduino Mini, Arduino Nano, Pro Mini).
3.3 Một số module mẫu
Đèn LED
Chức năng: thường dùng để làm đèn tín hiệu.
Cách lập trình: ta sẽ xem xét hai mạch sau để cùng bật một đèn LED.
Link: https://www.tinkercad.com/
things/3u2Pp26Xwzx
Hình 3.1: Điều khiển LED (tích cực cao)
3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 31
◮ Theo như hình trên, đèn LED sáng khi điện áp xuất ra từ Arduino
là điện áp cao.
1 void setup()
2 {
3 // Set pin 13 as OUTPUT
4 pinMode(13, OUTPUT);
5 }
6
7 void loop()
8 {
9 // Turn the LED on (HIGH is the voltage level)
10 digitalWrite(13, HIGH);
11 }
Link: https://www.tinkercad.com/
things/74dtpbF6plb
Hình 3.2: Điều khiển LED (tích cực thấp)
◮ Theo như hình trên, đèn LED sáng được khi điện áp xuất ra từ
Arduino là điện áp thấp.
1 void setup()
2 {
3 // Set pin 13 as OUTPUT
4 pinMode(13, OUTPUT);
5 }
6
7 void loop()
8 {
9 // Turn the LED on (HIGH is the voltage level)
10 digitalWrite(13, LOW);
11 }
◮ Để bật/tắt một thiết bị, chúng ta cần biết được đặc tính của thiết
bị đó là để hoạt động cần mức điện áp thấp (mức logic 0) hay
điện áp cao (mức logic 1).
Thông tin thêm: Tại sao điện trở nối tiếp LED trong hai ví dụ trên là
220Ω?
◮ Yêu cầu là cần bật một LED màu đỏ. Chúng ta cần xem các thông
số kỹ thuật liên quan đến LED đỏ là điện áp hoạt động và dòng
điện định mức. Sau quá trình tìm kiếm thì có được thông số như
sau: 𝑉𝐷 = 2V, I = 20mA.
◮ Dòng điện tối đa một chân của Arduino là 40 mA, mà dòng định
mức của LED là 20 mA thì một chân Arduino có thể tải được
trực tiếp một LED. Điện áp đang sử dụng là 5V mà điện áp 𝑉𝐷
của LED đỏ là 2V nên cần thêm một con trở mắc nối tiếp.
◮ Điện áp giữa hai đầu điện trở là 𝑈𝑅 = 5V – 𝑉𝐷 = 5V – 2V = 3V.
3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 32
◮ Dòng điện qua điện trở bằng dòng điện qua LED (do mắc nối
tiếp): 𝐼𝑅 = 𝐼𝐷. Do đây chỉ là đèn LED tín hiệu nên không cần quá
sáng, vì vậy chúng ta có thể giảm dòng điện lại để tiết kiệm điện
năng hơn. Thông thường thì để đèn LED ở khoảng 10 – 15 mA.
Trong ví dụ này chọn 15 mA.
◮ Giá trị điện trở: R = 𝑈𝑅 / 𝐼𝑅 = 5V / 15mA = 200Ω.
◮ Vì trên thị trường điện trở 200Ω không thông dụng, mà điện trở
220Ω lại thông dụng hơn nên chọn giá trị điện trở 220Ω.
Module Relay
Chức năng: dùng để bật tắt các thiết bị có công suất lớn/điện áp
lớn/dòng điện lớn.
Sơ đồ chân:
◮ Hiện nay, trên thị trường có rất nhiều loại module relay khác
nhau với sơ đồ nguyên lý khác nhau (cách ly hoặc không cách
ly/tích cực mức thấp hoặc cao), điện áp hoạt động khác nhau
(5V, 12V hoặc 24V) nên khi sử dụng ta cần chú ý đọc kỹ hướng
dẫn trước khi sử dụng.
◮ Thông thường, module sẽ có 2 domino (hoặc 1 header và 1
domino) để điều khiển module. Một domino để cấp nguồn (sẽ
có 2 chân DC+ và DC- hoặc GND và VCC) và tín hiệu điều khiển
bật tắt relay (IN). Domino còn lại dùng để điều khiển các thiết
bị công suất với 3 ngõ COM (cổng chung), NO (normal open
– thường mở) và NC (normal close – thường đóng). Nếu relay
được kích (hoạt động) thì COM sẽ nối với NO, ngược lại nếu
relay không được kích thì COM sẽ nối với NC.
Hình 3.3: Cách kết nối với Arduino
Mô phỏng Link: https://www.tinkercad.com/
things/lc8CaKsOHyI
Cách lập trình: tương tự như lập trình với LED.
1 void setup()
2 {
3 pinMode(13, OUTPUT);
4 }
5
6 void loop()
3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 33
Hình 3.4: Cách kết nối với Arduino (mô
phỏng)
7 {
8 digitalWrite(13, HIGH);
9 delay(1000); // Wait for 1000 millisecond(s)
10 digitalWrite(13, LOW);
11 delay(1000); // Wait for 1000 millisecond(s)
12 }
Nút nhấn (button)
Chức năng: dùng để chọn chế độ hoạt động, nhận một tín hiệu điều
khiển từ người dùng mà vi điều khiển có thể hiểu được.
Sơ đồ chân: nút nhấn được chia thành hai loại cơ bản là push button
và switch.
◮ Push button: sau khi tác động lực thì nút nhấn quay về trạng thái
ban đầu, trạng thái đóng/mở có thể được giữ hoặc không giữ.
Hình 3.5: Nút nhấn push-button. Nguồn
từ Wikimedia và Wikimedia.
◮ Switch: sau khi tác động lực để thay đổi trạng thái thì nút nhấn
không quay về trạng thái ban đầu, trạng thái của nút nhấn được
giữ khi ngừng tác dụng lực
Hình 3.6: DIP switch. Nguồn từ Wiki-
media.
3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 34
Hình 3.7: Nút nhấn tích cực thấp
Hình 3.8: Sơ đồ mạch điện của nút nhấn
(tích cực thấp)
Cách lập trình: chúng ta cùng xem xét hai mạch đọc nút nhấn khác
nhau.
Link: https://www.tinkercad.com/
things/3IN5fVNqBmI
Theo sơ đồ nguyên lý như hình 3.8:
◮ Xét trạng thái tại chân D2, giả sử chân D2 chỉ nhận mức tín hiệu
điện áp và không có dòng điện đi vào chân D2.
◮ Nếu S1 không được nhấn thì điện áp tại chân D2 là 5V. 𝐼𝑅1 = 0𝐴
vì đã giả sử không có dòng điện vào chân D2.
𝑈𝐷2 = 5𝑉 − 𝐼𝑅1
𝑅1 = 5𝑉 − 0(𝐴).10(𝑘Ω) = 5𝑉
◮ Ngược lại, nếu S1 được nhấn thì chân D2 được nối với 0V nên
điện áp đọc vào là 0V.
Trạng thái nút nhấn Giá trị điện áp Mức logic
Không nhấn 5V HIGH
Nhấn 0V LOW
1 int buttonState = 0;
2
3 void setup()
4 {
5 // Set pin 2 as INPUT
6 pinMode(2, INPUT);
3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 35
7 // Set pin 13 as OUTPUT
8 pinMode(13, OUTPUT);
9 }
10
11 void loop()
12 {
13 // Read the state of the pushbutton value
14 buttonState = digitalRead(2);
15 // Check if pushbutton is pressed. if it is, the
16 // buttonState is LOW
17 if (buttonState == LOW) {
18 // Turn LED on
19 digitalWrite(13, HIGH);
20 } else {
21 // Turn LED off
22 digitalWrite(13, LOW);
23 }
24 delay(10); // Delay a little bit to improve simulation performance
25 }
Hình 3.9: Nút nhấn tích cực ca
Link: https://www.tinkercad.com/
things/9tb62dzleah
Ta xét mạch điện thứ 2 như hình 3.10, ta có sơ đồ nguyên lý như sau:
◮ Tương tự, ta cũng xét trạng thái tại chân D2.
◮ Nếu S1 không được nhấn thì điện áp tại chân D2 là 0V. 𝐼𝑅1=0A
vì đã giả sử không có dòng điện vào chân D2.
𝑈𝐷2 = 𝐼𝑅1
𝑅1 = 0(𝐴).10(𝑘Ω) = 0𝑉
◮ Ngược lại, nếu S1 được nhấn thì chân D2 được nối với 5V nên
điện áp đọc vào là 5V.
Trạng thái nút nhấn Giá trị điện áp Mức logic
Không nhấn 0V LOW
Nhấn 5V HIGH
1 int buttonState = 0;
2
3 void setup()
4 {
5 // Set pin 2 as INPUT
6 pinMode(2, INPUT);
7 // Set pin 13 as OUTPUT
8 pinMode(13, OUTPUT);
9 }
3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 36
Hình 3.10: Sơ đồ mạch điện của nút
nhấn (tích cực cao)
10
11 void loop()
12 {
13 // Read the state of the pushbutton value
14 buttonState = digitalRead(2);
15 // Check if pushbutton is pressed. if it is, the
16 // buttonState is HIGH
17 if (buttonState == HIGH) {
18 // Turn LED on
19 digitalWrite(13, HIGH);
20 } else {
21 // Turn LED off
22 digitalWrite(13, LOW);
23 }
24 delay(10); // Delay a little bit to improve simulation performance
25 }
TIME 4
4.1 Giới thiệu . . . . . . . . . . . 37
4.2 Một số hàm thường dùng . 37
delay() . . . . . . . . . . . . . 37
delayMicroseconds() . . . . 38
millis() . . . . . . . . . . . . . 38
micros() . . . . . . . . . . . . 39
4.3 Ghi chú . . . . . . . . . . . . 39
4.1 Giới thiệu
Time (thời gian) trong lập trình vi điều khiển khá quan trọng vì giúp
cho vi điều khiển nhận biết thời gian cần phải thực hiện/thời gian đã
thực hiện một nhiệm vụ do người lập trình đưa ra.
4.2 Một số hàm thường dùng
delay()
Chức năng: dùng để dừng chương trình trong khoảng thời gian xác
định (đơn vị: milliseconds).
Cú pháp:
delay(ms);
◮ ms: số milliseconds cần dừng chương trình (kiểu dữ liệu: un-
signed long
Kết quả trả về: không.
Ví dụ:
1 delay(1000); // wait for a second
Lưu ý: trong thời gian thực hiện hàm delay thì vi điều khiển không
thực hiện bất cứ chương trình nào khác như đọc giá trị cảm biến, tính
toán hay thao tác trên các chân. Tuy nhiên, hàm delay sẽ không vô
hiệu các ngắt (interrupt) nên các giao tiếp bằng phần cứng vẫn hoạt
động (đường nhận dữ liệu), phát xung PWM hay trạng thái các Pin
không thay đổi. Vì vậy, chúng ta nên thật sự lưu ý khi dùng các hàm
delay. Ngoài ra, hàm delay cũng được dùng như một cách chống rung
phím bằng phần mềm.
4 TIME 38
delayMicroseconds()
Chức năng: dùng để dừng chương trình trong khoảng thời gian xác
định (đơn vị: microseconds).
Cú pháp:
delayMicroseconds(us);
◮ us: số microseconds cần dừng chương trình (kiểu dữ liệu: un-
signed long)
Kết quả trả về: không.
Ví dụ:
1 delayMicroseconds(1000); // wait for a millisecond
Lưu ý: hàm này làm việc chính xác khi us > 3 microsenconds.
millis()
Chức năng: trả về số milliseconds kể từ khi bắt đầu hoạt động. Giá trị
này sẽ bị tràn và quay về 0 khi đạt giá trị tối đa (khoảng 50 ngày).
Cú pháp:
time = millis();
Kết quả trả về: số milliseconds (kiểu dữ liệu: unsigned long).
Ví dụ:
1 unsigned long time;
2
3 void loop() {
4 time = millis();
5 delay(1000); // wait a second
6 }
Lưu ý: kiểu dữ liệu là unsigned long nên khi ta thực hiện phép toán thì
phải đảm bảo chung kiểu dữ liệu để không gây ra lỗi sai về giá trị tối
đa của các kiểu dữ liệu.
4 TIME 39
micros()
Chức năng: trả về số microseconds kể từ khi bắt đầu hoạt động. Giá
trị này sẽ bị tràn và quay về 0 khi đạt giá trị tối đa (khoảng 70 phút).
Cú pháp:
time = micros();
Kết quả trả về: số microseconds (kiểu dữ liệu: unsigned long).
Ví dụ:
1 unsigned long time;
2
3 void loop() {
4 time = micros();
5 delay(1000); // wait a second
6 }
4.3 Ghi chú
Khi sử dụng các hàm delay thì ta cần phải cân nhắc có nên sử dụng
hay không vì nó sẽ làm ảnh hưởng đến tốc độ chương trình. Ta cũng
có thể sử dụng các phương pháp khác để thay thế các hàm delay như
tính toán thời gian dựa vào việc lấy các hàm giá trị thời gian thực.
UART 5
5.1 Giới thiệu . . . . . . . . . . . 40
5.2 Một số hàm thường dùng . 40
Serial.begin() . . . . . . . . . 40
Serial.end() . . . . . . . . . . 41
Serial.print()/Serial.println() 41
Serial.write() . . . . . . . . . 42
Serial.availableForWrite() . 43
Serial.available() . . . . . . . 43
Serial.read() . . . . . . . . . . 44
Một số hàm khác . . . . . . . 44
Lưu ý . . . . . . . . . . . . . . 45
5.3 Một số module mẫu . . . . . 45
Module bluetooth HC-05 . . 45
Giao tiếp giữa 2 Arduino . . 47
5.4 Lời kết . . . . . . . . . . . . . 49
5.1 Giới thiệu
UART là một chuẩn giao tiếp nối tiếp để giao tiếp giữa các thiết bị với
nhau. (Lý thuyết về UART sẽ được giải thích ở quyển 2)
Trong Arduino, UART được dùng để giao tiếp giữa board Arduino với
các module khác có cùng giao thức hoặc giữa board Arduino với các
board vi điều khiển khác.
Giao thức UART gồm 2 dây tín hiệu là TX và RX. Khi 2 board muốn
giao tiếp với nhau thì phải mắc chéo (nghĩa là TX board này sẽ nối với
RX board kia, RX board này nối với TX board kia).
Một thông số quan trọng là tốc độ truyền (baudrate), các thông số này
là những số được quy định trước 1200, 2400, 4800, 9600, 19200, 38400,
57600, 115200,...
Một số thông số liên quan đến giao tiếp UART: baudrate, số stop
bit (1/2), số bytes dữ liệu (5/6/7/8(/9)), kiểm tra lỗi parity (odd-
/even/none).
5.2 Một số hàm thường dùng
Serial.begin()
Chức năng: thiết lập baudrate (và các thông số khác) cho giao tiếp nối
tiếp. Đây là thiết lập baudrate cho module phần cứng (chân 0 và 1 trên
vi điều khiển). Thông số khác như số lượng data, parity và stop bits
(giá trị mặc định là 8 data bits, no parity và 1 stop bit).
Cú pháp:
Serial.begin(speed);
Serial.begin(speed, config);
◮ Serial: đối tượng cổng nối tiếp (serial port)
◮ speed: baudrate (bits per second – kiểu dữ liệu: long)
◮ config: SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1
(giá trị mặc định), SERIAL_5N2, SERIAL_6N2, SERIAL_7N2, SE-
RIAL_8N2, SERIAL_5E1, SERIAL_6E1, SERIAL_7E1, SERIAL_-
8E1, SERIAL_5E2, SERIAL_6E2, SERIAL_7E2, SERIAL_8E2, SE-
RIAL_5O1, SERIAL_6O1, SERIAL_7O1, SERIAL_8O1, SERIAL_-
5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2
5 UART 41
Kết quả trả về: không.
Ví dụ:
void setup() {
// opens serial port, sets data rate to 9600 bps
Serial.begin(9600);
}
void loop() {}
Serial.end()
Chức năng: ngừng giao tiếp nối tiếp.
Cú pháp:
Serial.end();
Kết quả trả về: không.
Serial.print()/Serial.println()
Chức năng: in dữ liệu ra thông qua cổng nối tiếp với các ký tự nhìn
thấy được (human-readble ASCII text). Với lệnh Serial.println() thì sau
dữ liệu in ra thì có kèm theo 2 ký tự: ký tự trả về đầu dòng (a carriage
return character - ASCII 13 – ’r’) và ký tự xuống hàng (a newline
character – ASCII 10 – ’n’).
Cú pháp:
Serial.print(val);
Serial.print(val, format);
◮ Serial: đối tượng cổng nối tiếp
◮ val: giá trị cần in ra (với bất kỳ kiểu dữ liệu nào)
◮ format: định dạng dữ liệu của val (xem ví dụ)
Kết quả trả về: số bytes dữ liệu được ghi ra cổng nối tiếp (kiểu dữ liệu:
size_t).
Ví dụ: Link: https://www.tinkercad.com/
things/6L8YcnI8993
5 UART 42
1 void setup() {
2 Serial.begin(9600); // open the serial port at 9600 bps:
3 }
4
5 void loop() {
6 Serial.println("--------------------------");
7 Serial.print(78); // gives "78"
8 Serial.println(); // enter new line
9 Serial.print(1.23456); // gives "1.23"
10 Serial.println(); // enter new line
11 Serial.print('N'); // gives "N"
12 Serial.println(); // enter new line
13 Serial.print("Hello world."); // gives "Hello world."
14 Serial.println(); // enter new line
15
16 Serial.println(78, BIN); // gives "1001110"
17 Serial.println(78, OCT); // gives "116"
18 Serial.println(78, DEC); // gives "78"
19 Serial.println(78, HEX); // gives "4E"
20 Serial.println(1.23456, 0); // gives "1"
21 Serial.println(1.23456, 2); // gives "1.23"
22 Serial.println(1.23456, 4); // gives "1.2346"
23 delay (1000); // wait a sencond
24 }
Serial.write()
Chức năng: ghi dữ liệu nhị phân (binary data) thông qua cổng nối
tiếp. Dữ liệu này được gửi dạng byte hoặc chuỗi các bytes.
Cú pháp:
Serial.write(val);
Serial.write(str);
Serial.write(buf, len);
◮ Serial: đối tượng cổng nối tiếp
◮ val: giá trị cần gửi đi – 1 byte
◮ str: chuỗi các ký tự gửi đi – chuỗi các bytes
◮ buf: mảng các byte cần gửi
◮ len: chiều dài chuỗi dữ liệu cần gửi
Kết quả trả về: số bytes dữ liệu được ghi ra cổng nối tiếp (kiểu dữ liệu:
size_t).
Ví dụ:
1 void setup() {
2 Serial.begin(9600); // open the serial port at 9600 bps:
3 }
4
5 void loop() {
5 UART 43
6 Serial.write(45); // send a byte with the value 45
7
8 int bytesSent = Serial.write(“hello”); /* Send the string “hello” and return the length of the string.*/
9 delay (1000); // wait a sencond
10 }
Lưu ý: nếu bộ đệm (buffer) đủ khoảng trống để truyền thì hàm Se-
rial.write() sẽ trả về trước khi tất cả các ký tự được gửi, các ký tự gửi
đi sẽ được lưu trữ trong bộ đệm. Nếu độ đệm truyền bị đầy thì hàm
này sẽ khóa (block) mãi cho đến khi đủ khoảng trống cho việc truyền.
Để tránh trường hợp bị khóa (block) thì nên kiểm tra trước số byte còn
trống trong bộ đệm bằng hàm Serial.availableForWrite().
Serial.availableForWrite()
Chức năng: số bytes còn lại trong bộ đệm truyền.
Cú pháp:
Serial.availableForWrite();
◮ Serial: đối tượng cổng nối tiếp
Kết quả trả về: số bytes có sẵn để ghi.
Serial.available()
Chức năng: số bytes có sẵn trong bộ đệm nhận (bộ đệm tối đa là 64
bytes).
Cú pháp:
Serial.available();
◮ Serial: đối tượng cổng nối tiếp
Kết quả trả về: số bytes có sẵn để đọc.
Ví dụ:
1 void setup() {
2 Serial.begin(9600); // open the serial port at 9600 bps:
3 }
4
5 void loop() {
6 // reply only when you receive data:
7 if (Serial.available() > 0) {
8 // Read data at here. Please see next example.
9 }
10 }
5 UART 44
Serial.read()
Chức năng: đọc một byte dữ liệu đến.
Cú pháp:
Serial. read();
◮ Serial: đối tượng cổng nối tiếp
Kết quả trả về: ký tự đầu tiên trong chuỗi dữ liệu nhận được (kiểu dữ
liệu: int).
Ví dụ:
1 int incomingByte = 0; // for incoming serial data
2
3 void setup() {
4 // opens serial port, sets data rate to 9600 bps
5 Serial.begin(9600);
6 }
7
8 void loop() {
9 // send data only when you receive data:
10 if (Serial.available() > 0) {
11 // read the incoming byte:
12 incomingByte = Serial.read();
13
14 // say what you got:
15 Serial.print("I received: ");
16 Serial.println(incomingByte, DEC);
17 }
18 }
Một số hàm khác
Ngoài các hàm trên, Serial vẫn còn nhiều hàm khác sử dụng cho những
trường hợp khác
◮ if(Serial): kiểm tra cổng nối tiếp có sẵn sàng chưa.
◮ Serial.find(): tìm xem trong bộ đệm có ký tự muốn tìm hay không
bằng cách đọc dữ liệu trong bộ đệm đến khi tìm được ký tự
mong muốn.
◮ Serial.findUntil(): tìm chuỗi ký tự cần tìm có tồn tại trong bộ đệm
hay không bằng cách đọc dữ liệu trong bộ đệm đến khi tìm được
chuỗi mục tiêu hoặc chuỗi để kết thúc sự tìm kiếm.
◮ Serial.flush(): đợi cho việc truyền hoàn tất cho tất cả các ký tự
trong bộ đệm.
5 UART 45
◮ Serial.parseFloat(): trả về giá trị có kiểu dấu chấm động phù hợp
đầu tiên trong bộ đệm. Các ký tự không phải số hoặc dấu trừ sẽ
bị bỏ qua. Hàm này sẽ chấm dứt khi gặp ký tự đầu tiên không
phải dấu chấm động hoặc hết thời gian (thời gian này được thiết
lập bởi hàm Serial.setTimeout()).
◮ Serial.parseInt(): tương tự như hàm Serial.parseFloat() nhưng giá
trị cần đọc là kiểu int.
◮ Serial.peek(): trả về ký tự tiếp theo trong bộ đệm mà không có xóa
nó đi ra khỏi bộ đệm.
◮ Serial.readBytes(): đọc về số byte dữ liệu được xác định trước.
◮ Serial.readBytesUntil(): đọc về số byte dữ liệu được xác định trước
và phát hiện ký tự kết thúc hoặc hết thời gian định trước.
◮ Serial.readString(): đọc dữ liệu trong bộ đệm dưới kiểu dữ liệu là
String.
◮ Serial.readStringUntil(): đọc dữ liệu trong bộ đệm dưới kiểu dữ
liệu là String cho đến khi ký tự kết thúc được phát hiện hoặc hết
thời gian định trước.
◮ Serial.setTimeout(): thiết lập thời gian tối đa cho việc đợi dữ liệu
từ cổng nối tiếp. Giá trị mặc định là 1 giây.
◮ serialEvent(): hàm này được gọi khi có dữ liệu đến
Để biết thêm thông tin chi tiết, vui lòng truy cập vào link này.
Lưu ý
Khi lập trình C/C++, chúng ta cần phân biệt ký tự ‘a’ và chuỗi “a”.
Trường hợp ‘a’ (dấu nháy đơn) thì đây là ký tự a có kích thước một
byte. Trường hợp “a” (dấu nháy kép) thì đây là chuỗi ký tự có kích
thước hai bytes vì cuối chuỗi phải có chứa 1 byte kết thúc chuỗi ‘0’.
Khi bạn nhập ‘ab’, compiler có thể ngầm hiểu đây là chuỗi mà không
báo lỗi, nếu compiler không hỗ trợ thì sẽ thông báo lỗi.
◮ ’r’ (CR – Cariage Return): di chuyển con trỏ về đầu hàng mà
không xuống dòng.
◮ ’n’ (LF – Line Feed): di chuyển con trỏ xuống hàng tiếp theo.
5.3 Một số module mẫu
Module bluetooth HC-05
Hình 5.1: Module bluetooth HC-05.
Nguồn từ Eprolabs
5 UART 46
Chức năng: là module chuyển đổi chuẩn giao tiếp nối tiếp sang blue-
tooth Giao tiếp bằng bluetooth là giao tiếp tầm
gần (dưới 10m).
. Khi máy tính kết nối module này thì máy tính nhận là một cổng
COM ảo.
Sơ đồ chân:
Hình 5.2: Kết nối module bluetooth HC-
05 với Arduino.
◮ Kết nối như hình 5.2, ta có thể không cần thiết phải nối chân
KEY với 3.3V.
◮ Thông số mặc định của bluetooth là HC-05, mật khẩu kết nối là
1234 và baudrate mặc định là 9600.
◮ Chúng ta có thể thay đổi cấu hình của module bluetooh bằng
chế độ AT command, baudrate cho chế độ này là 38400. Bạn có
thể tìm kiếm trên mạng bằng từ khóa “hc05 at commands”.
◮ Lưu ý, TX và RX phải được mắc chéo.
Cách lập trình:
1 int incomingByte = 0; // for incoming serial data
2
3 void setup() {
4 Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
5 }
6
7 void loop() {
8 Serial.println("minht57 lab");
9 // send data only when you receive data:
10 if (Serial.available() > 0) {
11 // read the incoming byte:
12 incomingByte = Serial.read();
13
14 // say what you got:
15 Serial.print("I received: ");
16 Serial.println(incomingByte, DEC);
17 }
18 delay(1000);
19 }
Lưu ý: đây là chuẩn bluetooth 2.0 nên chỉ giao tiếp 1 – 1 (tức là một
master và 1 slave giao tiếp với nhau). Nếu bạn muốn tìm hiểu về nhiều
module giao tiếp với nhau có thể tham khảo với từ khóa “module
bluetooth 4.0” hoặc “module bluetooth BLE”.
Thông tin thêm:
5 UART 47
◮ Để hai thiết bị có thể giao tiếp được với nhau thì cần phải có dây
kết nối giữa chúng. Trong trường hợp cần sự linh hoạt và không
cần dây kết nối rờm rà thì cần phải có một giao thức không
dây để truyền tải dữ liệu như bluetooth, wifi,. . . Vì việc kết nối
với phần cứng để truyền nhận tín hiệu không dây thường phức
tạp, trải qua nhiều bước cấu hình nên các nhà sản xuất đã chế
tạo các module bluetooth/wifi và giao tiếp với các module này
thông qua các chuẩn giao tiếp phổ biến trên vi điều khiển như
UART/SPI.
◮ Giao tiếp với các module này thường thông qua tập lệnh "AT
command". AT (ATtention) command là bộ lệnh hướng dẫn khi
sử dụng để giao tiếp với module. Ví dụ khi làm việc với module
HC-05, chúng ta muốn thay đổi tên bluetooth thì công việc cần
làm là: vào chế độ AT command → gửi chuỗi "AT+NAME=AHIHI_-
DONGOC".
Giao tiếp giữa 2 Arduino
Ví dụ 1: Link: https://www.tinkercad.com/
things/kSSCq6eIkV7
board Arduino 1 sẽ gửi một chuỗi ký tự sang board Arduino
2. Khi board Arduino 2 nhận được sẽ in ra ngoài.
Hình 5.3: Giao tiếp nối tiếp giữa 2 board
Arduino
Chương trình Arduino 1
1 void setup() {
2 Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
3 }
4
5 void loop() {
6 Serial.println("minht57 lab, Vietnam");
7 delay(1000);
8 }
Chương trình Arduino 2
5 UART 48
1 char incomingByte = 0; // for incoming serial data
2
3 void setup() {
4 Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
5 }
6
7 void loop() {
8 // send data only when you receive data:
9 while (Serial.available() > 0) {
10 // read the incoming byte:
11 incomingByte = Serial.read();
12 Serial.print(incomingByte);
13 }
14 }
Ví dụ 2: Link: https://www.tinkercad.com/
things/lxCfz5KzuRK
board Arduino 1 sẽ đọc tín hiệu nút nhấn và gửi trạng thái
của nút nhấn sang board Arduino 2. Board Arduino 2 sẽ bật/tắt đèn
LED theo trạng thái nút nhấn nhận được từ board Arduino 1.
Hình 5.4: Giao tiếp nối tiếp giữa 2 Ar-
duino
Chương trình Arduino 1
1 int buttonState = 0;
2
3 void setup() {
4 // Set pin 2 as INPUT
5 pinMode(2, INPUT);
6 // Open serial port, sets data rate to 9600 bps
7 Serial.begin(9600);
8 }
9
10 void loop() {
11 // Read the state of the pushbutton value
12 buttonState = digitalRead(2);
13 // Check if pushbutton is pressed. If it is, the buttonState is HIGH
14 if (buttonState == HIGH) {
15 Serial.println('1');
5 UART 49
16 }
17 else
18 {
19 Serial.println('2');
20 }
21
22 delay(10); // Delay a little bit to improve simulation performance
23 }
Chương trình Arduino 2
1 char string;
2
3 void setup() {
4 // Set pin 13 as OUTPUT
5 pinMode(13, OUTPUT);
6 // Opens serial port, sets data rate to 9600 bps
7 Serial.begin(9600);
8 }
9
10 void loop() {
11 /*
12 // send data only when you receive data:
13 while (Serial.available() > 0) {
14 // read the incoming byte:
15 incomingByte = Serial.read();
16
17 }*/
18 string = Serial.read();
19 if (string == '1') {
20 // Turn LED on
21 digitalWrite(13, HIGH);
22 }
23 else if (string == '2') {
24 // Turn LED off
25 digitalWrite(13, LOW);
26 }
27 }
5.4 Lời kết
UART (hay giao tiếp nối tiếp) là một chuẩn giao tiếp rất phổ biến và
thông dụng trong lập trình vi điều khiển vì tính đơn giản để sử dụng
truyền dữ liệu nhỏ.
Chúng ta nên phân biệt rằng giao tiếp nối có rất nhiều chuẩn và UART
là một trong số đó nên cẩn thận khi nói các khái niệm:
◮ UART là giao tiếp nối tiếp (đúng).
◮ Giao tiếp nối tiếp chỉ là UART (sai).
5 UART 50
Có một chuẩn giao tiếp cũng khá phổ biến trong công nghiệp là
RS232/RS485, vì mức điện áp hoạt động của 2 chuẩn giao tiếp là khác
nhau nên khi vi điều khiển giao tiếp với các module có chuẩn giao
tiếp này thì chúng ta cần thêm một module chuyển đổi từ UART sang
RS232/RS485.
Lưu ý rằng đối tượng Serial là đối tượng hoạt động với ngoại vi phần
cứng bên trong chip (chân 0 và chân 1) và một số board Arduino chỉ
có 1 module giao tiếp nối tiếp, nếu ứng dụng của chúng ta muốn hoạt
động nhiều module giao tiếp nối tiếp hơn thì có các bạn có thể tham
khảo thư viện “SoftwareSerial”10
10: Các chức năng cơ bản của hai thư
viện Serial và SoftwareSerial là giống
nhau.
. Khi bạn sử dụng thư viện này thì
không có các chức năng liên quan đến phần cứng như interrupt hay
event.
ANALOG 6
6.1 Giới thiệu . . . . . . . . . . . 51
6.2 Một số hàm thường dùng . 52
analogRead() . . . . . . . . . 52
analogReference() . . . . . . 53
analogWrite() . . . . . . . . . 54
6.3 Một số module mẫu . . . . . 55
Biến trở . . . . . . . . . . . . . 55
Module điều khiển động cơ
L298N . . . . . . . . . . . . . . . 56
6.4 Lời kết . . . . . . . . . . . . . 62
6.1 Giới thiệu
Tín hiệu số (digital signal) là tín hiệu mà được thể hiện bằng một chuỗi
con số rời rạc. Tại bất kỳ thời điểm nào, nó chỉ có một giá trị trong tập
giá trị hữu hạn của nó. Các mạch điên tử số thông dụng mà chúng ta
thường thấy thì có 2 mức điện áp 0V và VCC. Người ta gọi là tín hiệu
nhị phân (binary signal).
Hình 6.1: Tín hiệu số (digital). Nguồn từ
Wikimedia.
Tín hiệu tương tự (analog signal) là tín hiệu có giá trị thay đổi liên tục
theo thời gian.
Hình 6.2: Tín hiệu tương tự (analog).
Nguồn từ Wikimedia.
Như chúng ta đã biết, vi điều khiển chỉ có thể xử lý bằng tín hiệu số.
Nhưng các tín hiệu trong môi trường đều là tín hiệu tương tự và vi
điều khiển muốn hiểu giá trị của các tín hiệu này thì phải có module
chuyển đổi từ tín hiệu tương tự sang tín hiệu số. Người ta đã nghiên
cứu ra một số phương pháp chuyển từ tín hiệu tương tự sang tín hiệu
số được gọi chung là ADC (Analo-Digital Converter). Ngược lại khi
muốn chuyển từ tín hiệu số sang tín hiệu tương tự thì có module DAC
(Digital-Analog Converter).
Ngoài hai module ADC và DAC, module điều chế độ rộng xung (PWM
- Pulse Width Modulation) cũng được Arduino hỗ trợ như một tính
năng analog dùng để xuất xung với độ rộng xung thay đổi trong một
chu kỳ xác định trước.
6 ANALOG 52
Hình 6.3: Xung PWM.
Điện áp trung bình được tín theo công thức 𝑉 = 𝑇𝑂𝑁
𝑇𝐶𝑦𝑐𝑙𝑒
𝑉𝐶𝐶 . Một số
ứng dụng phổ biến của PWM có thể kể đến như: điều khiền động cơ
(vi trí, tốc độ, moment), điều khiển điện áp (buck/boost/. . . ), thay đổi
độ sáng của đèn,. . .
Lưu ý: phân biệt điện áp trung bình và dạng tín hiệu của xung PWM,
Ví dụ khi PWM với 𝑇𝑂𝑁 =50% thì điện áp trung bình là 0.5𝑉𝐶𝐶 (khi lấy
VOM đo), nhưng dạng tín hiệu thực tế là dạng xung có 2 mức điện áp
là 0V và 𝑉𝐶𝐶 (dùng oscilloscope).
6.2 Một số hàm thường dùng
analogRead()
Chức năng: đọc giá trị analog từ một chân được chỉ định trước.
Board Arduino có chứa nhiều kênh analog (mỗi nguồn tín hiệu là
một kênh).
◮ Tín hiệu trả về có độ phân giải 10-bit, có nghĩa là vi điều khiển
đọc điện áp từ 0V – VCC thì giá trị trả về từ 0 đến 1023 (210 − 1).
Nếu tín hiệu điện áp nhỏ hơn 0V thì giá trị đọc được vẫn là 0,
tương tự nếu điện áp lớn VCC thì giá trị trả về là 1023.
◮ Giá trị ngưỡng điện áp tham chiếu có thể thay đổi khi sử dụng
hàm analogReference().
◮ Các board Arduino như UNO, Nano, Mini, Mega thì thời gian
đọc tín hiệu analog ngõ vào khoảng 100 us.
6 ANALOG 53
Board Operating voltage Usable pins Max resolution
Uno 5V A0..A5 10 bits
Mini, Nano 5V A0..A7 10 bits
Mega, Mega2560 5V A0..A14 10 bits
Micro 5V A0..A11 10 bits
Leonardo 5V A0..A11 10 bits
Zero 3.3V A0..A5 12 bits
Due 3.3V A0..A11 12 bits
Cú pháp:
analogRead(pin);
◮ pin: chân analog muốn đọc.
Kết quả trả về: giá trị analog đọc được.
Ví dụ:
1 /* potentiometer wiper (middle terminal) connected to analog pin 3 outside
2 leads to ground and +5V */
3 int analogPin = A3;
4 // variable to store the value read
5 int val = 0;
6
7 void setup() {
8 Serial.begin(9600); // setup serial
9 }
10 void loop() {
11 val = analogRead(analogPin); // read the input pin
12 Serial.println(val); // debug value
13 }
Lưu ý: nếu chân analog không được kết nối với bất kỳ nguồn tín hiệu
nào thì giá trị trả về sẽ bị dao động không biết trước.
analogReference()
Chức năng: cấu hình điện áp tham chiếu cho chân analog ngõ vào.
Cú pháp:
analogReference(type);
◮ type: loại tham chiếu được sử dụng.
• Arduino AVR Boards (Uno, Mega, etc.)
* DEFAULT: điện áp 5V nếu dùng board Arduino 5V
hoặc điện áp 3.3V nếu dùng board Arduino 3.3V.
* INTERNAL: điện áp tham chiếu nội, bằng 1.1V trên
chip ATmega168 or ATmega328P và 2.56V trên chip
ATmega8 (không có sẵn trên board Arduino Mega).
6 ANALOG 54
* INTERNAL1V1: điện áp tham chiếu nội 1.1V (chỉ có
trên board Arduino Mega).
* INTERNAL2V56: điện áp tham chiếu nội 2.56V (chỉ có
trên board Arduino Mega).
* EXTERNAL: sử dụng điện áp trên chân AREF để tham
chiếu ( từ 0 đến 5V).
• Arduino SAMD Boards (Zero, etc.)
* AR_DEFAULT: điện áp tham chiếu 3.3V
* AR_INTERNAL: điện áp tham chiếu nội 2.23V
* AR_INTERNAL1V0: điện áp tham chiếu nội 1.0V
* AR_INTERNAL1V65: điện áp tham chiếu nội 1.65V
* AR_INTERNAL2V23: điện áp tham chiếu nội 2.23V
* AR_EXTERNAL: sử dụng điện áp trên chân AREF để
tham chiếu.
• Arduino SAM Boards (Due)
* AR_DEFAULT: giá trị mặc định là 3.3V, đây là điện áp
tham chiếu duy nhất trên board Arduino Due.
Kết quả trả về: không.
Lưu ý:
◮ Sau thay đổi điện áp tham chiếu, vài giá trị trả về đầu tiên sẽ
không chính xác.
◮ Không sử dụng điện áp tham chiếu ngoài (thông qua chân AREF)
với mức điện áp nhỏ hơn 0V hay lớn hơn 5V.
◮ Nếu sử dụng điện áp tham chiếu ngoài, chúng ta cần thiết lập
điện áp tham chiếu là EXTERNAL trước khi gọi analogRead().
Bằng không thì sẽ gây ngắn mạch điện áp tham chiếu nội và điện
áp tham chiếu ngoại gây ra “hư hỏng” vi điều khiển.
analogWrite()
Chức năng: ghi ra dạng sóng PWM đến chân được chỉ định.
◮ Sau khi gọi hàm analogWrite(), chân được chỉ định sẽ tạo ra
sóng PWM với duty cycle được chỉ định mãi tới khi gọi hàm
analogWrite() lần nữa. Tần số PWM ở hầu hết các chân là 490Hz,
riêng chân 5 và 6 trên Uno (hoặc các board tương tự) có tần số
xấp xỉ 980Hz.
Cú pháp:
analogWrite(pin, value);
◮ pin: chân để ghi xung PWM.
◮ value: duty cycle, giá trị từ 0 đến 255.
Kết quả trả về: không.
Ví dụ: thiết lập độ sáng đèn tương ứng với giá trị biến trở.
6 ANALOG 55
1 // LED connected to digital pin 9
2 int ledPin = 9;
3 // potentiometer connected to analog pin 3
4 int analogPin = 3;
5 // variable to store the read value
6 int val = 0;
7
8 void setup() {
9 pinMode(ledPin, OUTPUT); // sets the pin as output
10 }
11
12 void loop() {
13 val = analogRead(analogPin); // read the input pin
14 /* analogRead values go from 0 to 1023, analogWrite values from 0 to 255 */
15 analogWrite(ledPin, val / 4);
16 }
6.3 Một số module mẫu
Biến trở
Hình 6.4: Biến trở xoay. Nguồn từ Wiki-
media và electronics-tutorials.
Sơ đồ chân:
◮ Biến trở có 3 chân (như hình 6.4) với giá trị là R, giá trị điện trở
𝑅12 và 𝑅23 sẽ thay đổi giá trị khi xoay núm vặn.
𝑅12 + 𝑅23 = 𝑅
◮ Nếu ta nối chân 1 và 3 của biến trở vào nguồn điện 5V, ta có
mạch chia áp với giá trị điện áp tại chân số 2 thay đổi từ 0V đến
5V.
Link: https://www.tinkercad.com/
things/8jh2P36bep4
Hình 6.5: Arduino và biến trở
6 ANALOG 56
Cách lập trình:
1 int sensorValue = 0;
2 float voltage = 0;
3
4 void setup()
5 {
6 pinMode(A0, INPUT);
7 Serial.begin(9600);
8 }
9
10 void loop()
11 {
12 // Reads the input on analog pin 0:
13 sensorValue = analogRead(A0);
14 // Prints out the value you read:
15 Serial.println(sensorValue);
16
17 // Calculates voltages
18 voltage = sensorValue * 5.0 / 1023;
19 Serial.print("Volt (V): ");
20 Serial.println(voltage, 2);
21 Serial.println("------------");
22
23 delay(10); // Delay a little bit to improve simulation performance
24 }
Lưu ý:
◮ Biến trở có nhiều hình dạng khác nhau (như hình 6.6) và cách
hoạt động khác nhau nhưng mục đích chung vẫn là thay đổi giá
trị điện trở.
◮ Biến trở có nhiều giá trị điện trở tối đa khác nhau nên tùy vào
ứng dụng mà ta chọn giá trị điện trở phù hợp.
Hình 6.6: Biến trở các loại. Nguồn từ
Wikimedia.
Module điều khiển động cơ L298N 12
12: Bài viết lấy nguồn từ howtomecha-
tronics
Chức năng:
◮ Để điều khiển tốc độ động cơ (chạy nhanh hay chậm) thì chúng
ta cần thay đổi giá trị điện áp cho động cơ và động cơ là một
thiết bị tiêu thụ điện với điện áp cao lẫn dòng điện cao nên việc
vi điều khiển điều khiển trực tiếp động cơ là không thể. Để hỗ
trợ vấn đề này, chúng ta cần một mạch lái (mạch điều khiển –
6 ANALOG 57
driver) động cơ để nhận tín hiệu điều khiển từ vi điều khiển và
cho ngõ ra điện áp tương ứng.
◮ Mạch điều khiển động cơ L298N là một module thông dụng điều
khiển 2 động cơ DC 12V riêng biệt.
Hình 6.7: Module điều khiển động cơ
L298N. Nguồn từ howtomechatronics.
Sơ đồ chân:
◮ Hai domino MotorA và MotorB được nối với động cơ.
◮ Chân 12V-GND để cấp nguồn cho động cơ.
◮ Chân 5V-GND để cấp nguồn cho mạch điều khiển động cơ.
Chúng ta có thể không cấp nguồn 5V thì có thể dùng jumper để
lấy điện áp từ nguồn 12V.
Hình 6.8: Chi tiết các chân module.
◮ Các chân ENA, IN1, IN2 để điều khiển MotorA, tương tự các
chân ENB, IN3, IN4 để điều khiển MotorB.
◮ Các chân INx để điều khiển chiều quay của động cơ, chân ENx
để cấp xung thay đổi tốc độ động cơ.
Hình 6.9: Chiều của dòng điện khi IC
L298N hoạt động.
Cách lập trình:
◮ Dùng biến trở để thay đổi tốc độ động cơ:
1 /* Arduino DC Motor Control - PWM | H-Bridge | L298N - Example 01
2
3 by Dejan Nedelkovski, www.HowToMechatronics.com
4 */
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf
ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf

Contenu connexe

Similaire à ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf

Hướng dẫn tự học Linux
Hướng dẫn tự học LinuxHướng dẫn tự học Linux
Hướng dẫn tự học LinuxNguyễn Duy Nhân
 
Lap trinh plc s7 200 chau chi duc
Lap trinh plc s7 200 chau chi ducLap trinh plc s7 200 chau chi duc
Lap trinh plc s7 200 chau chi ducNguyễn Hải Sứ
 
Ltnc giao trinh 2017
Ltnc giao trinh 2017Ltnc giao trinh 2017
Ltnc giao trinh 2017TaiTranQuang
 
Kien truc-co-ban-cua-stm32-arm-cortex-m3
Kien truc-co-ban-cua-stm32-arm-cortex-m3Kien truc-co-ban-cua-stm32-arm-cortex-m3
Kien truc-co-ban-cua-stm32-arm-cortex-m3Huynh MVT
 
Toàn tập về orcad
Toàn tập về orcadToàn tập về orcad
Toàn tập về orcadDavid Trần
 
Fx3u 3uc programming manual (vietnamese) fixed
Fx3u 3uc programming manual (vietnamese) fixedFx3u 3uc programming manual (vietnamese) fixed
Fx3u 3uc programming manual (vietnamese) fixedQuang Bách
 
Thiet ke lan_wan
Thiet ke lan_wanThiet ke lan_wan
Thiet ke lan_wanGió Lào
 
Thiet ke lan_wan
Thiet ke lan_wanThiet ke lan_wan
Thiet ke lan_wanHuu Phan
 
Phát triển thuật toán tự triển khai cho hệ thống đa robot giám sát môi trường...
Phát triển thuật toán tự triển khai cho hệ thống đa robot giám sát môi trường...Phát triển thuật toán tự triển khai cho hệ thống đa robot giám sát môi trường...
Phát triển thuật toán tự triển khai cho hệ thống đa robot giám sát môi trường...Man_Ebook
 
Tài liệu lập trình PLC Mitsubishi.pdf
Tài liệu lập trình PLC Mitsubishi.pdfTài liệu lập trình PLC Mitsubishi.pdf
Tài liệu lập trình PLC Mitsubishi.pdfMan_Ebook
 
C C N A Tieng Viet
C C N A Tieng  VietC C N A Tieng  Viet
C C N A Tieng VietHuy Le
 
Cisco router huong dan cau hinh cac tinh nang co ban
Cisco router huong dan cau hinh cac tinh nang co banCisco router huong dan cau hinh cac tinh nang co ban
Cisco router huong dan cau hinh cac tinh nang co banDat Nguyen
 

Similaire à ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf (20)

Hướng dẫn tự học Linux
Hướng dẫn tự học LinuxHướng dẫn tự học Linux
Hướng dẫn tự học Linux
 
Tu hoc su dung linux
Tu hoc su dung linuxTu hoc su dung linux
Tu hoc su dung linux
 
Luận án: Nghiên cứu kỹ thuật tách tín hiệu đường lên trong hệ thống Massive MIMO
Luận án: Nghiên cứu kỹ thuật tách tín hiệu đường lên trong hệ thống Massive MIMOLuận án: Nghiên cứu kỹ thuật tách tín hiệu đường lên trong hệ thống Massive MIMO
Luận án: Nghiên cứu kỹ thuật tách tín hiệu đường lên trong hệ thống Massive MIMO
 
Laptrinhplc
LaptrinhplcLaptrinhplc
Laptrinhplc
 
Lap trinh plc s7 200 chau chi duc
Lap trinh plc s7 200 chau chi ducLap trinh plc s7 200 chau chi duc
Lap trinh plc s7 200 chau chi duc
 
Taose
TaoseTaose
Taose
 
Ltnc giao trinh 2017
Ltnc giao trinh 2017Ltnc giao trinh 2017
Ltnc giao trinh 2017
 
Kien truc-co-ban-cua-stm32-arm-cortex-m3
Kien truc-co-ban-cua-stm32-arm-cortex-m3Kien truc-co-ban-cua-stm32-arm-cortex-m3
Kien truc-co-ban-cua-stm32-arm-cortex-m3
 
Vô tuyến nhận thức hợp tác cảm nhận phổ trong môi trường pha đinh
Vô tuyến nhận thức hợp tác cảm nhận phổ trong môi trường pha đinhVô tuyến nhận thức hợp tác cảm nhận phổ trong môi trường pha đinh
Vô tuyến nhận thức hợp tác cảm nhận phổ trong môi trường pha đinh
 
ĐỒ ÁN - ROBOT HÚT BỤI SỬ DỤNG ARDUINO.docx
ĐỒ ÁN - ROBOT HÚT BỤI SỬ DỤNG ARDUINO.docxĐỒ ÁN - ROBOT HÚT BỤI SỬ DỤNG ARDUINO.docx
ĐỒ ÁN - ROBOT HÚT BỤI SỬ DỤNG ARDUINO.docx
 
Gt thiet ke lan wan
Gt thiet ke lan wanGt thiet ke lan wan
Gt thiet ke lan wan
 
Toàn tập về orcad
Toàn tập về orcadToàn tập về orcad
Toàn tập về orcad
 
Fx3u 3uc programming manual (vietnamese) fixed
Fx3u 3uc programming manual (vietnamese) fixedFx3u 3uc programming manual (vietnamese) fixed
Fx3u 3uc programming manual (vietnamese) fixed
 
Luận Văn Kiến Trúc Hệ Thống Của Arm Cortex.doc
Luận Văn Kiến Trúc Hệ Thống Của Arm Cortex.docLuận Văn Kiến Trúc Hệ Thống Của Arm Cortex.doc
Luận Văn Kiến Trúc Hệ Thống Của Arm Cortex.doc
 
Thiet ke lan_wan
Thiet ke lan_wanThiet ke lan_wan
Thiet ke lan_wan
 
Thiet ke lan_wan
Thiet ke lan_wanThiet ke lan_wan
Thiet ke lan_wan
 
Phát triển thuật toán tự triển khai cho hệ thống đa robot giám sát môi trường...
Phát triển thuật toán tự triển khai cho hệ thống đa robot giám sát môi trường...Phát triển thuật toán tự triển khai cho hệ thống đa robot giám sát môi trường...
Phát triển thuật toán tự triển khai cho hệ thống đa robot giám sát môi trường...
 
Tài liệu lập trình PLC Mitsubishi.pdf
Tài liệu lập trình PLC Mitsubishi.pdfTài liệu lập trình PLC Mitsubishi.pdf
Tài liệu lập trình PLC Mitsubishi.pdf
 
C C N A Tieng Viet
C C N A Tieng  VietC C N A Tieng  Viet
C C N A Tieng Viet
 
Cisco router huong dan cau hinh cac tinh nang co ban
Cisco router huong dan cau hinh cac tinh nang co banCisco router huong dan cau hinh cac tinh nang co ban
Cisco router huong dan cau hinh cac tinh nang co ban
 

ARDUINO_CHO_NGI_MI_BT_DU_Quyn_can (1).pdf

  • 1. ARDUINO CHO NGƯỜI MỚI BẮT ĐẦU Quyển cơ bản minht57 lab
  • 2. Lời nói đầu Vi điều khiển là một thiết bị có thể lập trình được nhằm thực hiện các tác vụ định sẵn. Thường việc lập trình này dành cho các kỹ sư có trình độ kỹ thuật nhất định và thường rất khó khăn đối với các bạn học sinh tiếp cận. Nhờ sự ra đời của nền tảng Arduino để đơn giản hóa việc tiếp cận với vi điều dành cho một cá nhân chưa biết gì về lập trình vi điều khiển mà vẫn có thể tạo ra hàng nghìn ứng dụng khác nhau. Quyển sách “Arduino cho người mới bắt đầu” (quyển cơ bản) được thiết kế cho các bạn chưa biết hoặc biết một ít về lập trình Arduino. Nội dung sách chủ yếu viết về định hướng suy nghĩ liên quan đến lập trình vi điều khiển nhằm mục tiêu phát triển lâu dài, tạo một nền tảng căn bản nhất cho các bạn làm quen với lập trình vi điều khiển. Bên cạnh đó, cung cấp cho bạn một số mẹo, thông tin liên quan đến lập trình vi điều khiển nói chung và Arduino nói riêng. Quyển sách bao gồm 4 phần đi từ linh kiện điện tử cơ bản đến lập trình những module quan trọng của Arduino. Phần 1 (Linh kiện điện tử cơ bản) sẽ cho chúng ta một số kiến thức cơ bản về các loại linh kiện điện tử thường dùng. Phần 2 (Lập trình C cơ bản) sẽ cung cấp một số kiến thức cơ bản nhất về lập trình C hỗ trợ cho lập trình cho vi điều khiển. Phần 3 (Một số module ngoại vi quan trọng) sẽ cung cấp những kiến thức cốt lõi trong lập trình vi điều khiển nói chung và Arduino nói riêng. Phụ lục nhằm cung cấp thêm những thông tin, mạch điện bổ trợ. minht57 lab
  • 3. Hướng dẫn đọc sách Bộ sách Arduino dành cho người mới bắt đầu gồm 3 quyển đi từ mức độ cơ bản đến chuyên sâu bao gồm quyển cơ bản, quyển rất cơ bản và quyển không còn là cơ bản. Bộ sách này sẽ cung cấp cho bạn một tư duy làm việc với một vi điều khiển hơn là chỉ thực hành Arduino đơn thuần. Bộ 3 quyển sách sẽ cung cấp cho bạn rất nhiều thông tin ở mức căn bản dưới dạng từ khóa và tóm tắt vấn đề (vì nếu giải thích vấn đề rõ ràng, chuyên sâu thì sẽ rất lan man và dài dòng). Nếu bạn quan tâm một vấn đề nào cụ thể thì cứ dựa vào những từ khóa đã được nêu ra và tìm hiểu thêm trên internet hoặc sách vở. Vì mục tiêu của quyển sách là hướng đến những bạn học lập trình Arduino định hướng chuyên sâu nên sách sẽ không tập trung vào từng module cảm biến, thiết bị chấp hành hay một dự án nào cụ thể. Mà cấu trúc sách đi theo hướng học một vi điều khiển tổng quát mà Arduino chỉ là một ví dụ cụ thể. Nếu bạn chưa có kiến thức về lập trình Arduino thì quyển cơ bản được khuyên là bạn đọc theo thứ tự các chương từ đầu đến cuối. Nếu một vấn đề mà bạn đã biết, bạn cũng được khuyên là đọc lướt qua để tránh bỏ sót những thông tin mà có thể bạn chưa biết (vì không phải tất cả các thông tin quan trọng đều được in đậm hoặc nhấn mạnh). Nếu bạn đã có kiến thức cơ bản về lập trình Arduino thì bạn có thể đọc một chương bất kỳ mà bạn quan tâm. Nếu có thời gian thì bạn nên xem những chương mà bạn đã biết để kiểm chứng cũng như xem liệu có kiến thức nào chưa biết hay không. Nếu bạn cảm thấy văn phong hoặc cách tiếp cận của quyển sách không phù hợp với bạn thì bạn có thể bỏ qua. Trong quá trình viết và biên soạn sách không thể tránh khỏi những sai sót về mặt nội dung cũng như hình thức. Nhóm tác giả rất mong nhận được sự góp ý của các bạn đọc để quyển sách ngày càng hoàn thiện hơn và có thể truyền tải nội dung đến với nhiều bạn đọc hơn. Xin cảm ơn bạn đọc. minht57 lab
  • 4. Mục lục Lời nói đầu ii Hướng dẫn đọc sách iii Mục lục iv LINH KIỆN ĐIỆN TỬ CƠ BẢN 1 1 ĐIỆN TỬ CƠ BẢN 2 1.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.2 Điện trở . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.3 Tụ điện . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.4 Cuộn cảm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.5 Diode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.6 Transistor BJT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.7 MOSFET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.8 IC logic cần biết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.9 Op-amp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 LẬP TRÌNH C CƠ BẢN 12 2 LẬP TRÌNH C CƠ BẢN 13 2.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.2 Các thành phần cơ bản trong ngôn ngữ C . . . . . . . . . . . . . . . . . 13 2.3 Các hệ đếm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.4 Biểu thức và phép toán . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.5 Một số hàm C thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . 16 Hàm if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Hàm switch ... case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Vòng lặp for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Vòng lặp while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.6 Hàm/Chương trình con . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.7 Mảng một chiều . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.8 Con trỏ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 MỘT SỐ MODULE NGOẠI VI QUAN TRỌNG 27 3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 28 3.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 3.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 29 pinMode() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 digitalWrite() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 digitalRead() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
  • 5. 3.3 Một số module mẫu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Đèn LED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 Module Relay . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 Nút nhấn (button) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 4 TIME 37 4.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 4.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 37 delay() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 delayMicroseconds() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 millis() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 micros() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 4.3 Ghi chú . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 5 UART 40 5.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 5.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Serial.begin() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Serial.end() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Serial.print()/Serial.println() . . . . . . . . . . . . . . . . . . . . . . . . . 41 Serial.write() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Serial.availableForWrite() . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Serial.available() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Serial.read() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Một số hàm khác . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Lưu ý . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 5.3 Một số module mẫu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Module bluetooth HC-05 . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Giao tiếp giữa 2 Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 5.4 Lời kết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 6 ANALOG 51 6.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 6.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 52 analogRead() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 analogReference() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 analogWrite() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 6.3 Một số module mẫu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Biến trở . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Module điều khiển động cơ L298N . . . . . . . . . . . . . . . . . . . . . 56 6.4 Lời kết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 7 I2C 63 7.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 7.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Wire.begin() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Wire.requestFrom() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Wire.beginTransmission() . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Wire.endTransmission() . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Wire.write() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
  • 6. Wire.available() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Wire.read() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Wire.setClock() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Wire.onReceive() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Wire.onRequest() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 7.3 Một số module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Module IMU MPU6050 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Module thời gian thực RTC . . . . . . . . . . . . . . . . . . . . . . . . . 69 7.4 Lời kết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 8 SPI 73 8.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 8.2 Một số hàm thông dụng . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 SPISettings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 SPI.begin() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 SPI.end() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 SPI.beginTransaction() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 SPI.endTransaction() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 SPI.transfer(), SPI.transfer16() . . . . . . . . . . . . . . . . . . . . . . . . 76 SPI.usingInterrupt() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 8.3 Một số module thường dùng . . . . . . . . . . . . . . . . . . . . . . . . 76 Thẻ nhớ SD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 8.4 Lời kết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 9 NGẮT - INTERRUPT 81 9.1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 9.2 Một số hàm thường dùng . . . . . . . . . . . . . . . . . . . . . . . . . . 81 interrupts() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 noInterrupts() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 attachInterrupt() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 detachInterrupt() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 9.3 Lời kết . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 PHỤ LỤC 84 A MỘT SỐ MODULE THÔNG DỤNG KHÁC 85 A.1 Module đo khoảng cách SRF-05 . . . . . . . . . . . . . . . . . . . . . . . 85 A.2 Cảm biến độ ẩm đất . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 B MỘT SỐ HÀM KHÁC 89 B.1 Hàm I/O nâng cao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 B.2 Hàm liên quan đến toán . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 B.3 Hàm làm việc với chuỗi . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 C MỘT SỐ KHÁI NIỆM CĂN BẢN 91 C.1 Một số khái niệm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 C.2 Một số từ ngữ chuyên ngành . . . . . . . . . . . . . . . . . . . . . . . . 92 C.3 Một số kiến thức cần biết . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 Dòng sink/source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 Push-pull và Open drain IO . . . . . . . . . . . . . . . . . . . . . . . . . 93
  • 7. D TỔNG QUAN VỀ NỀN TẢNG ARDUINO 94 D.1 Phần cứng . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 D.2 Phần mềm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Cài đặt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Giới thiệu về Arduino IDE . . . . . . . . . . . . . . . . . . . . . . . . . . 99 E PHỤ LỤC MẠCH ĐIỆN 102 E.1 Mạch số 1: Diode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 E.2 Mạch số 2: Zener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 E.3 Mạch số 3: NPN Amplifier . . . . . . . . . . . . . . . . . . . . . . . . . . 104 E.4 Mạch số 4: Dùng BJT để đóng tắt relay . . . . . . . . . . . . . . . . . . . 105 E.5 Mạch số 5: IC số . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 Lời kết 109 Thông tin bản quyền 110
  • 8. LINH KIỆN ĐIỆN TỬ CƠ BẢN
  • 9. ĐIỆN TỬ CƠ BẢN 1 1.1 Giới thiệu . . . . . . . . . . . . 2 1.2 Điện trở . . . . . . . . . . . . . 2 1.3 Tụ điện . . . . . . . . . . . . . . 5 1.4 Cuộn cảm . . . . . . . . . . . . 7 1.5 Diode . . . . . . . . . . . . . . . 8 1.6 Transistor BJT . . . . . . . . . 8 1.7 MOSFET . . . . . . . . . . . . . 9 1.8 IC logic cần biết . . . . . . . 10 1.9 Op-amp . . . . . . . . . . . . 11 1.1 Giới thiệu Chương này sẽ cung cấp một số kiến thức cơ bản về một số linh kiện điện tử. Các linh kiện này sẽ là những linh kiện thường gặp trong suốt quá trình học điện tử. 1.2 Điện trở Linh kiện điện trở1 1: Điện trở và linh kiện điện trở là khác nhau về mặt khái niệm. Trong thực tế, linh kiện điện trở luôn được gọi tắt là điện trở. là linh kiện dùng để cản trở dòng điện (lưu ý, điện trở là đại lượng vật lý đặc trưng cho tính cản trở dòng điện của vật liệu). Công thức tính giá trị của điện trở: 𝑅 = 𝑈 𝐼 ◮ U: hiệu điện thế giữa hai đầu vật dẫn điện (đơn vị: Volt (V)) ◮ I: cường độ dòng điện đi qua vẫn dẫn (đơn vị: Ampere (A)) ◮ R: điện trở của vật dẫn (đơn vị: Ohm (Ω)) Công dụng ◮ Như tên điện trở, đây là linh kiện dùng để cản trở dòng điện nhưng thật ra điện trở có nhiều công dụng như giảm điện áp giữa tải với nguồn (ví dụ muốn cắm LED 3V vào nguồn 5V thì cần mắc thêm điện trở để giảm áp), làm điện áp tham chiếu bằng mạch chia áp, tạo các mạch khuếch đại/vi phân/tích phân (khi kết hợp với tụ điện, cuộn cảm, op-amp), mạch lọc thông thấp/thông cao (kết hợp với tụ điện, cuộn cảm), điện trở trong mạch đo dòng,.. . ◮ Điện trở là linh kiện chuyển năng lượng từ điện sang nhiệt (tức là trong lúc hoạt động sẽ sinh ra nhiệt) và thường lượng nhiệt này không có ích. Mỗi điện trở sẽ có một công suất tối đa nên khi dùng cần phải kiểm tra xem công suất mà điện trở phải chịu có vượt qua công suất mặc định của điện trở không. Các loại điện trở thường gặp: ◮ Điện trở cắm (through hole resistor) • Chúng ta đọc giá trị điện trở dựa vào bảng màu (resistor color code) • Mỗi điện trở cắm thông thường đều có vạch màu để xác định giá trị điện trở, đồng thời sai số của điện trở đó. Nếu là điện trở có 4 vạch màu thì 2 vạch đầu tiên là giá trị, vạch thứ 3 (vạch kế cuối) là hệ số nhân, vạch thứ 4 (vạch cuối
  • 10. 1 ĐIỆN TỬ CƠ BẢN 3 Hình 1.1: Hình ảnh điện trở chân cắm. Nguồn từ Wikimedia và Wikimedia. cùng) là sai số. Nếu là điện trở có 5 vạch màu thì 3 vạch điều tiên là giá trị, vạch thứ 4 (vạch kế cuối) là hệ số nhân, vạch thứ 5 (vạch cuối cùng) là sai số. • Ví dụ điện trở có 4 vòng màu lần lượt là nâu-đen-cam-vàng kim (gold): nâu có giá trị là 1, đen có giá trị là 0 thì giá trị 2 vạch đầu tiên là 10; cam là vạch thứ 3, cam có giá trị là 3 nên hệ số nhân là 103; vạch sai số là vàng kim, vàng kim có sai số là ±5%. Giá trị điện trở là 10𝑥103 ± 5% = 10𝑘Ω ± 5%. ◮ Một số mẹo • Một số khó khăn khi đọc giá trị điện trở là không xác định được màu của điện trở và vạch nào là vạch đầu tiên hay cuối cùng. Cách đơn giản là bạn đọc nhiều thì sẽ có kinh nghiệm đọc giá trị điện trở hoặc bạn xác định dựa vòng màu cuối cùng (vì thường vòng màu cuối cùng thường có màu đặc biệt như vàng kim (gold)/bạch kim (siliver)). • Ngoài bảng màu trên thì vẫn còn bảng màu đầy đủ hơn 2 2: Vì hình ảnh đó có bản quyền nên mình không sao chép vào tài liệu của mình được . Có 4 loại điện trở là 3/4/5/6 vòng màu, nhưng thường gặp nhất là điện trở 4 và 5 vòng màu. Ngoài ra, điện trở trên thực tế cũng có nhiều sai số khác nhau (0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 20) nhưng trên thị trường thường gặp loại điện trở 1% và 5%. • Thông thường, điện trở 4 vòng màu và thân hình màu vàng nhạt là điện trở 5%; điện trở 5 vòng màu và thân mình màu xanh dương nhạt thì là điện trở 1%. • Vậy việc đọc điện trở có cần thiết khi xung quanh chúng ta có quá nhiều thiết bị có thể đo được điện trở (như VOM/Multi- meter)? Câu trả lời là tùy thuộc vào bạn. Khi bạn tự đọc được giá trị điện trở thì có những lợi ích như sau: * Bạn có thể đọc được giá trị điện trở ngay lập tức mà không cần dụng cụ đo. * Đôi lúc chúng ta không thể đo giá trị của điện trở khi điện trở đã hàn trong mạch (vì điện trở sẽ mắc song song với mạch điện thì giá trị bạn đo giữa 2 đầu điện trở là giá trị của điện trở cần đo và giá trị điện trở của hệ thống tại 2 điểm đó) mà chúng ta cần ra bảng màu hoặc cách ly điện trở ra khỏi mạch điện. • Điện trở cắm thông thường sẽ có công suất 1/8W, 1/4W, 1/2W, 1W, 2W và 3W. • Điện trở sẽ có một số giá trị tiêu chuẩn phù hợp cho từng sai số khác nhau. Chúng sẽ có tiêu chuẩn giá trị điện trở (standard resistor value) được chia thành Ví dụ điện trở 5% sẽ có giá trị sau 1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1. Giá trị điện trở 5% sẽ từ 10Ω đến 22MΩ. Ví dụ với hệ số là 1.0 thì sẽ có các giá trị 10Ω, 100Ω, 1kΩ, 10kΩ, 100kΩ, 1MΩ, 10MΩ và tương tự cho những giá trị khác. E3 (>20%, có 3 giá trị điện trở), E6 (20%, có 6 giá trị điện trở), E12 (10%, có 12 giá trị điện trở), E24 (5%, có 24 giá trị điện trở), E48 (2%,
  • 11. 1 ĐIỆN TỬ CƠ BẢN 4 Hình 1.2: Bảng màu giá trị điện trở. Nguồn từ Wikimedia. có 48 giá trị điện trở), E96 (1%, có 96 giá trị điện trở), E192 (0.5%, 0.25%, <, có 192 giá trị điện trở). ◮ Điện trở dán SMD (SMD resistor) • Giá trị của điện trở dán sẽ được in lên trên bề mặt (nếu có thể). Các cách in trên điện trở có thể sẽ như sau: * 103 = 10𝑥103 = 10𝑘Ω * 100 = 10𝑥100 = 10Ω * 1001 = 100𝑥101 = 1𝑘Ω * 0 ≈ 0Ω * 5𝑅6 = 5.6Ω * 𝑅382 = 0.382Ω * 068 = 0.068Ω * 38𝐶 = 24.3𝑘Ω (tra theo chuẩn EIA-96) • Kích thước điện trở: * 0201: 0.024 x 0.012 inch, 1/20W * 0402: 0.04 x 0.02 inch, 1/16W * 0603: 0.06 x 0.03 inch, 1/10W * 0805: 0.08 x 0.05 inch, 1/8W * 1206: 0.12 x 0.06 inch, 1/4W * 1210: 0.12 x 0.10 inch, 1/2W * 1812: 0.18 x 0.12 inch, 1W * 2010: 0.20 x 0.10 inch, 3/4W * 2512: 0.25 x 0.12 inch, 1W
  • 12. 1 ĐIỆN TỬ CƠ BẢN 5 Hình 1.3: Điện trở dán SMD. Nguồn từ Wikimedia. • Một số mẹo: * Nếu thấy điện trở in 3 chữ số thì có thể là điện trở 5%, nếu in 4 chữ số thì có thể là điện trở 1% (lưu ý không phải lúc nào cũng đúng). ◮ Biến trở (potentiometer) Hình 1.4: Biến trở các loại. Nguồn từ Wikimedia và Wikimedia. • Biến trở là linh kiện có thể thay đổi được giá trị. Giá trị tối đa của điện trở thông thường được ghi trực tiếp lên điện trở. 1.3 Tụ điện Chúng ta có thể hiểu đơn giản tụ điện là một linh kiện có thể chứa được điện tích. Mỗi tụ điện sẽ có bản tụ ở 2 cực (được ngăn cách nhau bởi 1 lớp điện môi, mỗi loại lớp điện môi khác nhau sẽ có một loại tụ khác nhau) và các bản tụ này dùng để chứa điện tích. Tụ điện ngăn cản không cho dòng điện một chiều đi qua nhưng cho phép dòng điện xoay chiều đi qua (khái niệm đầy đủ hơn tại Wikipedia). Hình 1.5: Một số loại tụ điện. Nguồn từ Wikimedia. Một số thông số cần lưu ý khi làm việc với tụ: ◮ Điện dung C (đơn vị Farad - F) thể hiện khả năng tích điện của tụ. Điện dung càng lớn thì tích điện càng nhiều.
  • 13. 1 ĐIỆN TỬ CƠ BẢN 6 ◮ Điện áp làm việc tối đa U (đơn vị Volt - V) là điện áp rơi tối đa lên tụ có thể chịu được. Nếu điện áp hoạt động lớn hơn điện áp làm việc thì gây chập tụ và nổ. Khi chọn điện áp làm việc cho tụ hãy chọn tối thiểu có giá trị bằng 1.3 điện áp làm việc (ví dụ điện áp làm việc 12V thì nên chọn tụ có điện áp 12𝑉 ∗1.3 = 15.6 ≈ 16𝑉). ◮ Công thức tính điện lượng Q: 𝑄 = 𝐶𝑈 • Thể hiện lượng điện tích được tích tụ ở bề mặt của tụ điện. • Đơn vị là (Coulomd - C). ◮ Nhiệt độ làm việc (đơn vị C/F) là ngưỡng tối đa/tối thiểu của nhiệt độ mà tụ có thể làm việc được. Thông thường chúng ta không quan tâm đến thông số này, nhưng nếu thiết bị có chứa tụ làm việc trong các môi trường khắc nghiệt (lò hơi, phòng lạnh, ngoài trời,...) thì hãy chú ý đến nhiệt độ hoạt động của môi trường. ◮ Ngoài ra còn nhiều thông số khác nhưng tùy vào ứng dụng cụ thể mà bạn nên tìm hiểu thêm như hệ số biến đổi điện dung theo nhiệt độ/thời gian, dòng rò (leakage current), tần số làm việc, sai số (tolerance), ESR (Equivalent Series Resistance).. . Một số loại tụ phổ biến: ◮ Tụ hóa (Electrolytic Capacitors): là tụ điện phân cực. Điều lưu ý khi sử dụng tụ là không được gắn ngược chiều âm dương của tụ với nguồn (nếu ngược thì sẽ gây nổ). Hình 1.6: Các loại tụ hóa (từ trái sang phải): tụ hóa chân cắm (Radial lead Elec- trolytic Capacitors), tụ tantan (SMD Tan- talium Capacitor), tụ nhôm (Aluminum Electrolytic Capacitor) . Nguồn từ Wiki- media. • Tụ hóa thường có giá trị lớn (có thể từ vài chục nF đến vài F). Tụ có giá trị lớn được gọi là siêu tụ (supercapacitor). • Cực của tụ hóa thường được đánh dấu trên tụ (tụ hóa chân cắm/tụ nhôm sẽ đánh dấu cực âm, tụ tantalium sẽ đánh dấu cực dương). Hình 1.7: Hình ảnh ký hiệu cực trên tụ điện. Nguồn từ Wikimedia và Wikime- dia. ◮ Tụ gốm (ceramic capacitor) là tụ điện không phân cực, thường có giá trị nhỏ. • Nếu là tụ gốm loại cắm thì thường giá trị sẽ in lên tụ (đơn vị là pF). Nếu là tụ dán SMD thì giá trị tụ sẽ được ghi trên reel (cách đóng gói tụ khi bán).
  • 14. 1 ĐIỆN TỬ CƠ BẢN 7 Hình 1.8: Tụ hóa. Nguồn từ Wikimedia và Wikimedia. • Các thông tin cụ thể về điện áp hoạt động, giá trị tụ, sai số,... sẽ được ghi trên bao bì đóng gói của nhà sản xuất. Nếu bạn có được một con tụ không có thông tin thì rất khó để tìm được thông số của nó. • Kích thước của tụ cũng khá đa dạng. Bạn tham khảo thêm với từ khóa “EIA & Metric Standard Sizes” để biết thêm chi tiết. ◮ Những loại tụ khác bạn có thể tìm hiểu tại đây. ◮ Chi tiết về đặc tính tụ, bạn đọc tham khảo chuỗi bài tại electronics- tutorials 1.4 Cuộn cảm Cuộn cảm là linh kiện có khả năng sinh ra từ trường khi có dòng điện chạy qua. Cuộn cảm đặc trưng bằng độ tự cảm (từ dung) có đơn vị đo là Henry (H). Cuộn cảm sẽ cho dòng điện một chiều đi qua nhưng sẽ cản trở dòng điện xoay chiều. Hình 1.9: Một số loại cuộn cảm. Nguồn từ Wikimedia. Ghi chú: thông thường thì tác giả ít khi thấy dùng cuộn cảm với các ứng dụng cơ bản nên chúng ta sẽ không đi quá sâu ngoài khái niệm. Nếu bạn đọc đang làm liên quan đến lọc nhiễu, làm mạch nguồn buck/boost/..., mạch lọc tần số,... thì bạn nên đọc tài liệu chuyên ngành cụ thể để hiểu thêm về đặc tính của cuộn cảm vì lý thuyết về cuộn cảm nhìn chung là hơi khó so với mức căn bản.
  • 15. 1 ĐIỆN TỬ CƠ BẢN 8 1.5 Diode Diode là linh kiện bán dẫn chỉ cho phép dòng điện đi theo một chiều nhất định. Diode có 2 khối bán dẫn ghép với nhau là bán dẫn loại P và bán dẫn loại N. Dòng điện dương sẽ chạy từ P (anode) sang N (cathode). Hình 1.10: Một số loại diode (từ trái sang phải): diode thường, LED (Light Emitting Diode), diode zener . Nguồn từ Wikimedia. Các loại bán dẫn thường thấy: diode chỉnh lưu, zener, LED (Light Emitting Diode), Schottky Diode,... Ngoài ra vẫn còn nhiều loại diode khác như Gunn Diode, PIN diode, step recovery diode, laser diode, photo diode,. . . Một số thông số cần quan tâm: ◮ Điện áp phân cực thuận là điện áp tối thiểu để dòng điện có thể chạy qua diode (thường giá trị điện áp phân cực thuận từ 0.15V đến vài volt tùy vào từng loại diode, giá trị mà chúng ta hay nghe là 0.7V. Giá trị này thông thường phụ thuộc vào loại vật liệu được trộn vào để làm bán dẫn). ◮ Điện áp ngược cực đại là điện áp tối đa nếu mắc ngược cực của diode vào nguồn mà không ảnh hưởng gì. Nếu lớn hơn điện áp này thì diode sẽ bị đánh thủng (thường giá trị điện áp đánh thủng từ vài trăm đến vài nghìn volt). ◮ Dòng điện tối đa mà diode có thể tải được. ◮ Tần số đóng ngắt tối đa mà diode có thể đáp ứng được. 3 3: Bạn sẽ quan tâm thông số này khi làm việc với các mạch đóng ngắt có tần số (ví dụ như mạch nguồn xung). 1.6 Transistor BJT Transistor BJT là linh kiện bán dẫn chủ động thường được sử dụng như một phần tử khuếch đại (amplify) hoặc khóa điện tử (switch elec- tronic signals). Transistor là từ được kết hợp từ “transfer” và “resistor” vì nó sẽ chuyển đổi một tín hiệu yếu từ mạch điện có trở kháng thấp sang mạch điện có trở kháng cao (“The transistor is a semiconductor device which transfers a weak signal from low resistance circuit to high resistance circuit. The words trans mean transfer property and istor mean resistance property offered to the junctions.”) Transistor được ghép từ 3 lớp bán dẫn: N-P-N thì được gọi là NPN transistor, P-N-P thì được gọi là PNP transistor. Transistor có 3 cực là B (base), C (collector), E (emitter). Chiều mũi tên là chiều của dòng điện. Transistor sẽ có 3 vùng hoạt động lần lượt là vùng tắt, vùng khuếch đại và vùng bão hòa. Tùy vào ứng dụng mà bạn cần cấu hình mạch transistor phù hợp để transistor rơi vào đúng vùng hoạt động. Ví dụ khi bạn cho transistor hoạt động ở chế độ khuếch đại thì phải phân
  • 16. 1 ĐIỆN TỬ CƠ BẢN 9 Hình 1.11: Transistor BJT. Nguồn từ Wikimedia và Wikimedia. cực làm sao để điểm hoạt động của transistor rơi vào vùng đấy. Nếu bạn muốn transistor hoạt động như công tắc thì để transistor chuyển đổi giữa vùng tắt và vùng bão hòa (bạn đọc thấy hứng thú thì tìm hiểu thêm về lý thuyết transistor, mặc dù hay nhưng khó). Ký hiệu như hình 1.12. Hình 1.12: Ký hiệu BJT. Nguồn từ Wiki- media. Một số thông số cần lưu ý: điện áp phân cực thuận 𝑉𝐵𝐸 , điện áp tối đa hoạt động, dòng diện tối đa, tần số hoạt động. Tùy vào ứng dụng cụ thể mà bạn cần quan tâm đến những thông số nào (khi bạn tìm hiểu một ứng dụng cụ thể thì thông thường các tài liệu sẽ đề cập những thông số về transistor). Thông tin của transistor thì bạn nên tìm trên datasheet của sản phẩm. 1.7 MOSFET MOSFET là viết tắt của "Metal-Oxide Semiconductor Field-Effect Tran- sistor", là transistor hiệu ứng trường. Hình 1.13: Cấu tạo của MOSFET kênh N. Nguồn từ Wikimedia. MOSFET cũng có 2 loại là N-MOSFET và P-MOSFET. MOSFET có 3 cực được đặt tên là G (gate), S (source), D (drain). Ký hiệu như hình 1.14.
  • 17. 1 ĐIỆN TỬ CƠ BẢN 10 Hình 1.14: Ký hiệu của MOSFET kênh N (bên trái) và kênh P (bên phải). Nguồn từ Wikimedia. Ghi chú về sự khác nhau lớn nhất giữa transistor BJT và MOSFET: BJT điều khiển bằng dòng (current-driven device) tại cực B, trong khi đó MOSFET được điều khiển bằng áp (voltage-controlled device) được đặt vào cực G. 1.8 IC logic cần biết IC (viết tắt của Integrated Circuit) là một mạch điện thực hiện một chức năng nào đó được đóng gói sẵn trong một con chip. IC có nhiều loại khác nhau nhưng có thể chia thành 4 nhóm chính: ◮ Microcomputers là IC có chứa bộ vi xử lý. ◮ Memory là IC dùng để chứa dữ liệu. ◮ Standard logics ICs là IC để được thực hiện một chức năng cụ thể (chúng ta đề cập loại IC này trong đề mục này). ◮ Custom Logic ICs là IC được thực hiện theo nhu cầu của người dùng. Thông tin thú vị: mạch điện là sự kết hợp của transistor, tụ điện, điện trở, cuộn cảm,. . . Tất cả các thành phần này trong chip được làm bằng semiconductor trên một tấm bán dẫn gọi là wafer. IC logic thường có hai loại: TTL và CMOS. Cơ bản chức năng của các ICs này là giống nhau nhưng sẽ khác nhau nhau về một số đặc tính nhất định. ◮ TTL ICs (Transistor-transistor logic ICs) được chế tạo bằng BJT. ◮ CMOS ICs (Complementary metal oxide semiconductor ICs) được chế tạo từ một cặp p/n – MOSFET với nhau. ◮ Nếu điện áp hoạt động của chip là 5V: • TTL ICs: * Mức tín hiệu đầu vào: thấp (low) là 0 – 0.8V, cao (high) là 2 – 5V. * Mức tín hiệu đầu ra: thấp (low) là 0 – 0.5V, cao (high) là 2.7 – 5V. • CMOS ICs: * Mức tín hiệu đầu vào: thấp (low) là 0 – 1.5V, cao (high) là 3.5 – 5V. * Mức tín hiệu đầu ra: thấp (low) là 0 – 0.05V, cao (high) là 4.95 – 5V. Một dòng chip Standard logics ICs khá phổ biến là dòng 7400. Trên thị trường, chúng ta thường thấy có 74HCxx và 74LSxx. Sự khác nhau cơ bản: chip 74HCxx (CMOS – High speed) được chế tạo bằng công
  • 18. 1 ĐIỆN TỬ CƠ BẢN 11 nghệ CMOS, chip 74LSxx (Low power Shottky) được chế tạo bằng công nghệ TTL. (Ngoài ra còn có nhiều loại khác, tìm hiểu tại đây). Một số chip nên biết: ◮ 74x00: NAND 2 ngõ vào ◮ 74x01: NAND 2 ngõ vào có cực thu hở (open-collector). ◮ 74x02: NOR 2 ngõ vào ◮ 74x08: AND 2 ngõ vào ◮ 74x09: AND 2 ngõ vào có cực thu hở (open-collector) ◮ 74x14: cổng đảo (có Schmitt-trigger) ◮ 74x32: OR 2 ngõ vào ◮ 74x86: XOR 2 ngõ vào ◮ 74x595: thanh ghi dịch 8-bit, ngõ vào nối tiếp, ngõ ra song song ◮ 74x4511: BCD sang 7-segment (dùng với LED 7 đoạn) ◮ Ngoài ra còn rất nhiều loại khác, vui lòng đọc tại đây. 1.9 Op-amp Op-amp (operational amplifier – mạch khuếch đại thuật toán) là một mạch khuếch đại “DC-coupled” với hệ số khuếch đại cao, có đầu vào vi sai và thông thường có đầu ra đơn. Hình 1.15: Op-amp. Nguồn từ Wikime- dia. Ký hiệu: Hình 1.16: Ký hiệu của op-amp. Nguồn từ Wikimedia. ◮ V+: Đầu vào không đảo ◮ V-: Đầu vào đảo ◮ 𝑉𝑜𝑢𝑡 : Đầu ra ◮ 𝑉𝑆+: Nguồn cung cấp điện dương ◮ 𝑉𝑆−: Nguồn cung cấp điện âm Một số ứng dụng: op-amp có rất nhiều ứng dụng trong thực tế vì opamp có thể thực hiện nhiều phép toán đảo, cộng, trừ, nhân, chia, tích phân, đạo hàm tín hiệu. Mình sẽ liệt kê một số ứng dụng cơ bản mà bạn có thể gặp ở mức độ cơ bản: ◮ Mạch đệm đảo/không đảo. ◮ Mạch khuếch đại đảo/không đảo. ◮ Mạch khuếch đại vi sai (sẽ gặp trong ứng dụng cân sử dụng cảm biến loadcell). ◮ Mạch so sánh điện áp.
  • 19. LẬP TRÌNH C CƠ BẢN
  • 20. LẬP TRÌNH C CƠ BẢN 2 2.1 Giới thiệu . . . . . . . . . . . 13 2.2 Các thành phần cơ bản trong ngôn ngữ C . . . . . . . . . . . . 13 2.3 Các hệ đếm . . . . . . . . . . 14 2.4 Biểu thức và phép toán . . . 14 2.5 Một số hàm C thường dùng 16 Hàm if . . . . . . . . . . . . . 16 Hàm switch ... case . . . . . . 17 Vòng lặp for . . . . . . . . . . 20 Vòng lặp while . . . . . . . . 22 2.6 Hàm/Chương trình con . . . 23 2.7 Mảng một chiều . . . . . . . 24 2.8 Con trỏ . . . . . . . . . . . . . 25 2.1 Giới thiệu C là ngôn ngữ lập trình cấu trúc được sử dụng rộng rãi trong lập trình nhúng. Chương này sẽ giúp bạn đọc nắm được những kiến thức cơ bản cần thiết phục vụ cho lập trình vi điều khiển. 2.2 Các thành phần cơ bản trong ngôn ngữ C Từ khóa là từ có ý nghĩa xác định dùng để khai báo dữ liệu, viết hàm,... ◮ Ví dụ: if, else, for, switch,... Tên nhằm thể hiện rõ ý nghĩa của hằng, biến, mảng, con trỏ,... trong chương trình. Kiểu dữ liệu: STT Kiểu dữ liệu Chiều dài Ngưỡng giá trị 1 unsigned char 1 byte 0 → 255 2 char 1 byte -128 → 127 3 unsigned int 2 bytes (hoặc 4 bytes) 0 → 65535 (hoặc 0 → (232 − 1)) 4 int 2 bytes (hoặc 4 bytes) -32768 → 32768 (hoặc −231 → (231 − 1)) 5 unsigned short 2 bytes 0 → 65535 6 short 2 bytes -32768 → 32768 7 unsigned long 8 bytes 0 → 264 8 long 8 bytes −263 → (263 − 1) 9 float 4 bytes 3.4 ∗ 10−38 → 3.4 ∗ 10−38 10 double 8 bytes 1.7 ∗ 10−308 → 1.7 ∗ 10308 Khai báo và khởi tạo: ◮ Khai báo (declaration) int this_is_parameter; Biến this_is_parameter được khai báo với kiểu dữ liệu int. ◮ Khởi tạo (initialization) int this_is_parameter = 100; Biến this_is_parameter được khai báo với kiểu dữ liệu int và khởi tạo giá trị ban đầu là 100.
  • 21. 2 LẬP TRÌNH C CƠ BẢN 14 2.3 Các hệ đếm Các hệ đếm cơ bản trong lập trình C: hệ nhị phân (hệ 2 – binary), hệ bát phân (hệ 8 – octal), hệ thập phân (hệ 10 – decimal) và hệ thập lục phân (hệ 16 – hexadecimal) Decimal Hex Octal Binary 0 0 0 0 1 1 1 1 2 2 2 10 3 3 3 11 4 4 4 100 5 5 5 101 6 6 6 110 7 7 7 111 8 8 10 1000 9 9 11 1001 10 A 12 1010 11 B 13 1011 12 C 14 1100 13 D 15 1101 14 E 16 1110 15 F 17 1111 16 10 20 1 0000 32 20 40 10 0000 64 40 100 100 0000 128 80 200 1000 0000 256 100 400 1 0000 0000 512 200 1000 10 0000 0000 1024 400 2000 100 0000 0000 Hệ đếm là cách biểu diễn con số dưới các dạng khác nhau. Ví dụ số mười được biểu diễn dưới dạng nhị phân là 10102, dưới dạng bát phân là 128, dưới dạng thập phân là 1010 và dưới dạng thập lục phân là 𝐴16. Trong cuộc sống thường ngày, chúng ta dùng hệ thập phân, còn vi điều khiển thi dùng hệ nhị phân. Từ đó trong lập trình, chúng ta dùng hệ thập phân khi chúng ta muốn người đọc hiểu nhanh chóng (human- readable), dùng hệ nhị phân khi muốn hiểu vi điều khiển đang hiểu như thế nào, thập lục phân khi muốn viết gọn một chuỗi số nhị phân. 2.4 Biểu thức và phép toán Biểu thức là sự phối hợp của những toán tử và toán hạng. a = b + 1
  • 22. 2 LẬP TRÌNH C CƠ BẢN 15 Giá trị b được cộng cho 1, sau đó kết quả được gán lại cho a. 5 5: Trong phép toán, b và 1 là toán hạng, + là toán tử. index += 2; Giá trị index được cộng cho 2, sau đó kết quả được gán lại cho index (đây là cách viết gọn). Các loại phép toán: ◮ Phép toán số học: cộng (+), trừ (-), nhân (*), chia (/), chia lấy dư (%). 10 + 8 / 2 = 14 12 % 5 = 2 ◮ Phép quan hệ: lớn hơn (>), lớn hơn hoặc bằng (>=), bé hơn (<), bé hơn hoặc bằng (<=), bằng (==) và khác (!=). 10 > 2 → có giá trị 1 (đúng) 20 == 30 → có giá trị 0 (sai) ◮ Phép toán luận lý: phủ định (!), và (&&) và hoặc (||). 5 && (8 < 9) → có giá trị 1 (đúng) 1 || 0 → có giá trị 1 (đúng) 1 && 0 → có giá trị 0 (sai) ◮ Phép toán trên bit (bitwise): và (&), hoặc (|), xor (ˆ), dịch trái («), dịch phải (»), đảo (∼). bit x bit y x&y x|y x ˆ y x 0 0 0 0 0 1 0 1 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 0 210 >> 1 = 00102 >> 1 = 00012 210 << 1 = 00102 << 1 = 01002 ◮ Các phép toán khác: phép gán (=), phép gán kết hợp (+=, -=, *=, /=, %=, »=, «=, &=, |=, ˆ=), phép tăng giảm (++, - -). ◮ Độ ưu tiên của các phép toán6 6: Bạn đọc tham khảo tại dayn- hauhoc.com. :
  • 23. 2 LẬP TRÌNH C CƠ BẢN 16 Độ ưu tiên Các phép toán Trình tự 1 () [] -> Trái → Phải 2 ! ∼ & * ++ – Phải → Trái 3 * / % Trái → Phải 4 + - Trái → Phải 5 << >> Trái → Phải 6 < <= > >= Trái → Phải 7 == != Trái → Phải 8 & Trái → Phải 9 ^ Trái → Phải 10 | Trái → Phải 11 && Trái → Phải 12 || Trái → Phải 13 ? : Phải → Trái 14 = += -= *= /= %= <<= >>= &= ^= |= Phải → Trái 2.5 Một số hàm C thường dùng Hàm rẽ nhánh có điều kiện (if ... else if ... else ....) 1 if (biểu thức luận lý 1) { 2 <khối lệnh 1> 3 } 4 else if (biểu thức luận lý 2) { 5 <khối lệnh 2> 6 } 7 ... 8 else if (biểu thức luận lý n) { 9 <khối lệnh n> 10 } 11 else { 12 <khối lệnh n + 1> 13 } ◮ Hàng 1..3: nếu (if) biểu thức luận lý 1 đúng thì thực hiện khối lệnh 1 và thoát khỏi cấu trúc if ◮ Hàng 4..6: ngược lại nếu (else if) biểu thức luận lý 2 đúng thì thực hiện khối lệnh 2 và thoát khỏi cấu trúc if ◮ Hàng 8..10: ngược lại (else if) nếu biểu thức luận lý n đúng thì thực hiện khối lệnh n và thoát khỏi cấu trúc if ◮ Hàng 11..13: ngược lại (else) thực hiện khối lệnh n + 1. Lưu ý: ◮ Từ khóa if, else if, else phải viết thường. ◮ Biểu thức luận lý đúng khi kết quả khác 0 (≠ 0) và sai khi kết quả bằng 0 (== 0). Một số dạng rút gọn của cấu trúc if
  • 24. 2 LẬP TRÌNH C CƠ BẢN 17 1 if (biểu thức luận lý 1) { 2 <khối lệnh 1> 3 } hoặc 1 if (biểu thức luận lý 1) { 2 <khối lệnh 1> 3 } 4 else { 5 <khối lệnh 2> 6 } Ví dụ: Link: https://onlinegdb.com/Sy_ yoAEWP 1 #include <stdio.h> 2 3 int main() 4 { 5 int a; 6 printf("Please add a: "); 7 scanf("%d", &a); 8 9 if(a>0) { 10 printf("%d is greater than 0", a); 11 } 12 else if (a == 0) { 13 printf("%d is equal to 0", a); 14 } 15 else { 16 printf("%d is less than 0", a); 17 } 18 19 return 0; 20 } Kết quả: Please add a: 6 6 is greater than 0 Hàm rẽ nhánh có điều kiện (switch ... case ...) 1 switch (biểu thức) { 2 case giá trị 1: 3 <khối lệnh 1>
  • 25. 2 LẬP TRÌNH C CƠ BẢN 18 4 break; 5 case giá trị 2: 6 <khối lệnh 2> 7 break; 8 ... 9 case giá trị n: 10 <khối lệnh n> 11 break; 12 default: 13 <khối lệnh n+1> 14 } ◮ Hàng 1: trong trường hợp biểu thức có giá trị là ◮ Hàng 2..4: giá trị 1 (case) thì thực hiện khối lệnh 1 và thoát ra khỏi cấu trúc switch (break), ◮ Hàng 5..7: hoặc giá trị 2 (case) thì thực hiện khối lệnh 2 và thoát ra khỏi cấu trúc switch (break), ◮ Hàng 9..11: hoặc giá trị n (case) thì thực hiện khối lệnh n và thoát ra khỏi cấu trúc switch (break), ◮ Hàng 12..13: ngược lại (default) thì thực hiện khối lệnh n+1. Lưu ý: ◮ Từ khóa switch, case, break, default phải viết thường. ◮ Biểu thức phải có kết quả là giá trị nguyên. Ví dụ: Link: https://onlinegdb.com/ ryB1TC4bw 1 #include <stdio.h> 2 3 int main() 4 { 5 int month = 0; 6 printf("Please input month: "); 7 scanf("%d", &month); 8 9 switch(month) 10 { 11 case 1: 12 printf("Januaryn"); 13 break; 14 case 2: 15 printf("Februaryn"); 16 break; 17 case 3: 18 printf("Marchn"); 19 break; 20 case 4: 21 printf("Apriln"); 22 break; 23 case 5: 24 printf("Mayn"); 25 break; 26 case 6: 27 printf("Junen"); 28 break; 29 case 7:
  • 26. 2 LẬP TRÌNH C CƠ BẢN 19 30 printf("Julyn"); 31 break; 32 case 8: 33 printf("Augustn"); 34 break; 35 case 9: 36 printf("Septembern"); 37 break; 38 case 10: 39 printf("Octobern"); 40 break; 41 case 11: 42 printf("Novembern"); 43 break; 44 case 12: 45 printf("Decembern"); 46 break; 47 default: 48 printf("Input is wrongn"); 49 } 50 51 switch(month) 52 { 53 case 1: 54 case 2: 55 case 3: 56 printf("Month %d is in Q1n", month); 57 break; 58 case 4: 59 case 5: 60 case 6: 61 printf("Month %d is in Q2n", month); 62 break; 63 case 7: 64 case 8: 65 case 9: 66 printf("Month %d is in Q3n", month); 67 break; 68 case 10: 69 case 11: 70 case 12: 71 printf("Month %d is in Q4n", month); 72 break; 73 } 74 75 return 0; 76 } Kết quả: Please input month: 2 February Month 2 is in Q1
  • 27. 2 LẬP TRÌNH C CƠ BẢN 20 Vòng lặp for 1 for (biểu thức 1; biểu thức 2; biểu thức 3) { 2 <khối lệnh> 3 } ◮ Hàng 1: đầu tiên sẽ thực hiện biểu thức 1, ◮ Hàng 1..2: kiểm tra biểu thức 2 nếu đúng thì thực hiện khối lệnh. Sau đó thực hiện biểu thức 3. ◮ Hàng 1..2: kiểm tra biểu thức 2 nếu vẫn còn đúng thì thực hiện khối lệnh. Sau đó thực hiện biểu thức 3. ◮ Hàng 1..2: kiểm tra biểu thức 2 nếu vẫn còn đúng thì thực hiện khối lệnh. Sau đó thực hiện biểu thức 3. ◮ Hàng 1: Kiểm tra biểu thức 2 nếu sai thì thoát khỏi vòng for. Lưu ý: ◮ Từ khóa for phải viết thường. Một số từ khóa đi với vòng lặp: ◮ break: dùng để thoát vòng lặp gần nhất chứa nó. ◮ continue: dùng để tiếp tục vòng lặp gần nhất, bỏ qua các lâu lệnh phía sau lệnh continue. Ví dụ 1: Link: https://onlinegdb.com/ HJCwC0E-P 1 #include <stdio.h> 2 3 int main() 4 { 5 int idx = 0; 6 for(idx = 0; idx < 10; idx++) { 7 printf("Loop %dn", idx); 8 } 9 printf("---n"); 10 for(idx = 0; idx < 10; idx++) { 11 printf("Loop %dr", idx); 12 } 13 return 0; 14 } Kết quả: Loop 0 Loop 1 Loop 2 Loop 3 Loop 4 Loop 5 Loop 6 Loop 7 Loop 8 Loop 9
  • 28. 2 LẬP TRÌNH C CƠ BẢN 21 --- Loop 9 Ví dụ 2: Link: https://onlinegdb.com/ B19tyyBZD 1 #include <stdio.h> 2 3 int main() 4 { 5 int break_point = -1; 6 printf("Input a point [0..9]: "); 7 scanf("%d", &break_point); 8 if ((break_point > 9) || (break_point < 0)) { 9 printf("Input is wrong"); 10 return 0; 11 } 12 13 int idx = 0; 14 for(idx = 0; idx < 10; idx++) { 15 if(idx == break_point) { 16 break; 17 } 18 printf("Loop %dn", idx); 19 } 20 printf("---n"); 21 for(idx = 0; idx < 10; idx++) { 22 if(idx == break_point) { 23 continue; 24 } 25 printf("Loop %dn", idx); 26 } 27 return 0; 28 } Kết quả: Input a point [0..9]: 5 Loop 0 Loop 1 Loop 2 Loop 3 Loop 4 --- Loop 0 Loop 1 Loop 2 Loop 3 Loop 4 Loop 6 Loop 7 Loop 8 Loop 9
  • 29. 2 LẬP TRÌNH C CƠ BẢN 22 Vòng lặp while 1 while(điều kiện){ 2 <khối lệnh>; 3 } ◮ Hàng 1..2: Nếu điều còn đúng thì thực hiện khối lệnh, nếu điều kiện sai thì thoát ra khỏi vòng lặp. Ví dụ: Link: https://onlinegdb.com/ SyByxkH-v 1 #include <stdio.h> 2 3 int main() 4 { 5 int idx = 10; 6 while(idx--) { 7 printf("Loop %dn", idx); 8 } 9 printf("---n"); 10 idx = 10; 11 while(--idx) { 12 printf("Loop %dn", idx); 13 } 14 15 return 0; 16 } Kết quả: Loop 9 Loop 8 Loop 7 Loop 6 Loop 5 Loop 4 Loop 3 Loop 2 Loop 1 Loop 0 --- Loop 9 Loop 8 Loop 7 Loop 6 Loop 5 Loop 4 Loop 3 Loop 2 Loop 1
  • 30. 2 LẬP TRÌNH C CƠ BẢN 23 Thông tin Trong vi điều khiển luôn cần một vòng lập vô tận (while(1), while(true), for(;;)) để vi điều khiện lặp đi lặp lại một công việc nào đó chúng ta lập trình (thật ra để vi điều khiển luôn được kiểm soát nằm trong hoạt động của chương trình). Nếu chúng ta lập trình trên vi điều khiển mà không liên quan đến hệ điều hành (ví dụ RTOS) thì chỉ cần duy nhất một vòng lập vô tận duy nhất vì khi vào vòng lặp thì không bao giờ thoát ra được. Khi chúng ta sử dụng các vòng lập for, while, do...while thì cần phải kiểm soát số lần lặp để tránh trường hợp rơi vào vòng lập vô tận. Lưu ý về vòng lặp vô tận: ◮ Vòng lặp vô tận 1 unsigned char index = 100; 2 for(index = 100; index >= 0; index--) { 3 ... 4 } ◮ Vòng lặp được thực hiện 101 lần 1 char index = 100; 2 for(index = 100; index >= 0; index--) { 3 ... 4 } 2.6 Hàm/Chương trình con Hàm được dùng để thực hiện một công việc nào đó cụ thể mà người dùng muốn đặt ra. Người lập trình thường viết chương trình lớn thành nhiều chương trình con để cho chương trình đỡ phức tạp và dễ dàng trong đọc hiểu, bảo trì code. Chương trình con có thể gọi một chương trình con khác. Nếu chương trình con gọi chính nó thì gọi là phương pháp đệ quy. Lưu ý khi lập trình đệ quy là phải có điều kiện thoát, nếu không chương trình sẽ lập vô tận và sinh ra các kết quả không mong muốn. Ví dụ 1: Link: https://onlinegdb.com/ rkLte1BZw 1 #include <stdio.h> 2 3 int sum(int a, int b) 4 { 5 return (a+b); 6 } 7 8 int main() 9 {
  • 31. 2 LẬP TRÌNH C CƠ BẢN 24 10 int result = sum(2,4); 11 printf("2 + 4 = %d", result); 12 13 return 0; 14 } Kết quả: 2 + 4 = 6 Ví dụ 2: Hàng 3 được gọi là prototype function. Prototype function bắt buộc khai báo khi chương trình con được định nghĩa sau hàm main. 1 #include <stdio.h> 2 3 int sum(int a, int b); 4 5 int main() 6 { 7 int result = sum(2,4); 8 printf("2 + 4 = %d", result); 9 10 return 0; 11 } 12 13 int sum(int a, int b) 14 { 15 return (a+b); 16 } Kết quả trả về giống như trên. 2.7 Mảng một chiều Mảng là tập hợp nhiều phần tử có chung kiểu dữ liệu. Chỉ số của các phần tử trong mảng luôn bắt đầu là 0 và tăng dần 1 đơn vị cho các phần tử tiếp theo. Ví dụ: tạo ra mảng 10 phần tử có địa chỉ liên tiếp nhau có chung kiểu dữ liệu là int. int array[10]; Để tham chiếu phần tử trong mảng, ta thực hiện cú pháp sau: int value = array[2]; Lưu ý Khi khai báo mảng cần phải xác định số lượng phần tử trong mảng nhằm tránh gây ra các sai sót về bộ nhớ.
  • 32. 2 LẬP TRÌNH C CƠ BẢN 25 Ví dụ: 1 #include <stdio.h> 2 3 #define NUMBER_OF_ARRAY 10 4 5 int main() 6 { 7 int array[NUMBER_OF_ARRAY]; 8 int idx = 0; 9 for(idx = 0; idx < NUMBER_OF_ARRAY; idx++) 10 { 11 array[idx] = 2 * idx; 12 } 13 14 printf("Value at index 2: %dn", array[2]); 15 16 array[2] *= 3; 17 18 printf("New value at index 2: %dn", array[2]); 19 20 return 0; 21 } Kết quả: Value at index 2: 4 New value at index 2: 12 2.8 Con trỏ Con trỏ sẽ chứa địa chỉ dùng để truy cập các biến/hàm thông qua địa chỉ này. Ví dụ: ◮ int par; → Khai báo biến par có kiểu dữ liệu là int. ◮ int * px; → Khai báo con trỏ sẽ trỏ đến ô nhớ có kiểu dữ liệu là int. ◮ px = &par; → Lấy địa chỉ của biến par gán cho con trỏ px. ◮ (* px) = 10; → Gán 10 vào ô nhớ được chứa trong con trỏ px (tức là gán giá trị cho par thông qua con trỏ px). Link: https://onlinegdb.com/ BkdMXyrWD 1 #include <stdio.h> 2 3 int main() 4 { 5 int parameter = 100; 6 7 printf("Value of parameter: %dn", parameter); 8 printf("Value of address of parameter: 0x%xn", &parameter); 9
  • 33. 2 LẬP TRÌNH C CƠ BẢN 26 10 int *px = &parameter; 11 printf("px is pointer. Value of px: 0x%xn", px); 12 printf("Value at address in pointer: %dn", (*px)); 13 14 (*px) = 10; 15 printf("New value at address in pointer: %dn", (*px)); 16 printf("Value of parameter: %dn", parameter); 17 18 return 0; 19 } Kết quả (lưu ý địa chỉ của biến parameter có thể không giống bên dưới): Value of parameter: 100 Value of address of parameter: 0x39d73204 px is pointer. Value of px: 0x39d73204 Value at address in pointer: 100 New value at address in pointer: 10 Value of parameter: 10
  • 34. MỘT SỐ MODULE NGOẠI VI QUAN TRỌNG
  • 35. GENERAL PURPOSE INPUT/OUTPUT – GPIO 3 3.1 Giới thiệu . . . . . . . . . . . 28 3.2 Một số hàm thường dùng . 29 pinMode() . . . . . . . . . . . 29 digitalWrite() . . . . . . . . . 29 digitalRead() . . . . . . . . . 30 3.3 Một số module mẫu . . . . . 30 Đèn LED . . . . . . . . . . . . 30 Module Relay . . . . . . . . . 32 Nút nhấn (button) . . . . . . 33 3.1 Giới thiệu General Purpose Input/Output (GPIO) là module dùng để đọc tín hiệu vào hay xuất tín hiệu ra một hoặc một số chân vi điều khiển. GPIO chỉ làm việc với 2 mức điện áp (điện áp thấp và điện áp cao) tương ứng với mức logic 1 (mức điện áp cao – tùy thuộc vào vi điều khiển: có thể là 1.8V, 3.3V, 5V,...) và mức logic 0 (mức điện áp thấp – thường là 0V). Xuất tín hiệu logic ra ngoài (output) là đưa điện áp mức cao (mức logic 1) hay điện áp mức thấp (mức logic 0). Đọc tín hiệu điện áp vào có kết quả là mức logic 0/1 (input) nhằm để xác định xem điện áp tại chân vi điều khiển tương ứng là mức thấp hay cao. Một số đặc tính khi chân của vi điều khiển được cấu hình là INPUT: cấu hình mặc định của các chân Arduino (Atmega) là input, vì thế chúng ta không cần khai báo khi hàm pinMode() khi sử dụng chúng như một chân input. Chân vi điều khiển được cấu hình bằng cách này sẽ ở trạng thái trở kháng cao (high-impedance state) – khoảng 100MΩ. Thêm trở kéo lên/xuống cho một chân của vi điều khiển khi được cấu hình là INPUT: một điều như không bắt buộc nhưng được xem là thông lệ (nên được xem là bắt buộc) là phải xác định trạng thái của chân (được cấu hình là input) nếu không có tín hiệu input từ ngoài vào. Việc này được thực hiện bằng cách thêm một điện trở kéo lên (lên mức điện áp cao - +5V đối với Arduino Atmega) hoặc điện trở kéo xuống (xuống đất – 0V). Giá trị điện trở này thường được dùng là 10 kΩ. Đặc tính của chân của vi điều khiển khi được cấu hình là INPUT_- PULLUP: bên trong vi điều khiển Atmega đã có một điện trở 20K được kéo lên nguồn có thể truy cập bằng phần mềm. Để truy cập được điện trở này thì ta khai báo pinMode() với biến là INPUT_PULLUP. Đặc tính của chân của vi điều khiển khi được cấu hình là OUTPUT: khi được cấu hình là OUTPUT thì chân này ở trạng thái trở kháng thấp (low-impedance state). Điều này có nghĩa là vi điều khiển có thể cung cấp một lượng dòng điện lớn (vài milliampere đến vài chục milliampere) ra ngoài. Các chân của vi điều khiển Atmega có thể dùng mạch kiểu source (cung cấp dòng điện dương) hoặc kiểu sink (cung cấp dòng điện âm) lên đến 40mA. Với lượng dòng điện này có thể đủ làm sáng LED (phải mắc nối tiếp điện trở để hạn dòng) hoặc một vài cảm biến nhưng không đủ dòng để bật tắt relay (rờ-le), motor. Lưu ý: việc ngắn mạch hoặc để các thiết bị tiêu thụ dòng cao trên chân của Arduino có thể hỏng một chân hoặc cả con vi điều khiển. Vì vậy,
  • 36. 3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 29 khi kết nối bất cứ thiết bị nào vào vi điều khiển phải xem xét đến dòng điện tiêu thụ. Ví dụ: dòng điện tiêu thụ của đèn LED khoảng 20mA, dòng tối đa mà một chân vi điều khiển chịu được dòng điện tối đa là 40mA.Một đèn LED có thể nối trực tiếp với chân vi điều khiển, nhưng không nên mắc song song từ 2 LED trở lên vì sẽ gây hại đến vi điều khiển (có thể chết chân hoặc chết cả vi điều khiển). Giải pháp là hãy dùng mạch đệm dòng hoặc dùng BJT/FET để điều khiển. 3.2 Một số hàm thường dùng pinMode() Chức năng: dùng để cấu hình một chân là input hay output. Cú pháp: pinMode(pin, mode); ◮ pin: chân ta muốn cấu hình ◮ mode: INPUT, OUTPUT hoặc INPUT_PULLUP Kết quả trả về: không. Ví dụ: thiết lập chân 13 là ouput. pinMode(13, OUTPUT); // sets the digital pin 13 as output Lưu ý: các chân analog vẫn có thể sử dụng như digital (input/out- put). digitalWrite() Chức năng: ghi mức logic HIGH (cao) hoặc LOW (thấp) ra chân được chỉ định. Cú pháp: digitalWrite(pin, value); ◮ pin: chân ta muốn ghi mức giá trị. ◮ value: HIGH hoặc LOW. Kết quả trả về: không. Lưu ý:
  • 37. 3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 30 ◮ Nếu không thiết lập pinMode() là OUTPUT và kết nối đèn LED vào pin này, khi gọi hàm digitalWrite(HIGH) thì đèn LED có thể sẽ sáng mờ. Nguyên nhân là ta không thiết lập pinMode là OUTPUT thì mặc định là INPUT và gọi hàm digitalWrite(HIGH) sẽ bật điện trở nội kéo lên nên sẽ xem như đèn LED mắc nối tiếp với trở hạn dòng có giá trị lớn. ◮ Các chân analog vẫn có thể sử dụng như chân digital (ngoại trừ chân A6, A7 của board Arduino Mini, Arduino Nano, Pro Mini). digitalRead() Chức năng: đọc giá trị logic từ chân được chỉ định. Cú pháp: digitalRead(pin); ◮ pin: chân mà ta muốn đọc Giá trị trả về: HIGH hoặc LOW. Lưu ý: ◮ Nếu chân được đọc tín hiệu mà không được nối vào đâu thì giá trị trả về có thể là HIGH hoặc LOW (và có thể thay đổi đột ngột). ◮ Các chân analog vẫn có thể sử dụng như chân digital (ngoại trừ chân A6, A7 của board Arduino Mini, Arduino Nano, Pro Mini). 3.3 Một số module mẫu Đèn LED Chức năng: thường dùng để làm đèn tín hiệu. Cách lập trình: ta sẽ xem xét hai mạch sau để cùng bật một đèn LED. Link: https://www.tinkercad.com/ things/3u2Pp26Xwzx Hình 3.1: Điều khiển LED (tích cực cao)
  • 38. 3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 31 ◮ Theo như hình trên, đèn LED sáng khi điện áp xuất ra từ Arduino là điện áp cao. 1 void setup() 2 { 3 // Set pin 13 as OUTPUT 4 pinMode(13, OUTPUT); 5 } 6 7 void loop() 8 { 9 // Turn the LED on (HIGH is the voltage level) 10 digitalWrite(13, HIGH); 11 } Link: https://www.tinkercad.com/ things/74dtpbF6plb Hình 3.2: Điều khiển LED (tích cực thấp) ◮ Theo như hình trên, đèn LED sáng được khi điện áp xuất ra từ Arduino là điện áp thấp. 1 void setup() 2 { 3 // Set pin 13 as OUTPUT 4 pinMode(13, OUTPUT); 5 } 6 7 void loop() 8 { 9 // Turn the LED on (HIGH is the voltage level) 10 digitalWrite(13, LOW); 11 } ◮ Để bật/tắt một thiết bị, chúng ta cần biết được đặc tính của thiết bị đó là để hoạt động cần mức điện áp thấp (mức logic 0) hay điện áp cao (mức logic 1). Thông tin thêm: Tại sao điện trở nối tiếp LED trong hai ví dụ trên là 220Ω? ◮ Yêu cầu là cần bật một LED màu đỏ. Chúng ta cần xem các thông số kỹ thuật liên quan đến LED đỏ là điện áp hoạt động và dòng điện định mức. Sau quá trình tìm kiếm thì có được thông số như sau: 𝑉𝐷 = 2V, I = 20mA. ◮ Dòng điện tối đa một chân của Arduino là 40 mA, mà dòng định mức của LED là 20 mA thì một chân Arduino có thể tải được trực tiếp một LED. Điện áp đang sử dụng là 5V mà điện áp 𝑉𝐷 của LED đỏ là 2V nên cần thêm một con trở mắc nối tiếp. ◮ Điện áp giữa hai đầu điện trở là 𝑈𝑅 = 5V – 𝑉𝐷 = 5V – 2V = 3V.
  • 39. 3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 32 ◮ Dòng điện qua điện trở bằng dòng điện qua LED (do mắc nối tiếp): 𝐼𝑅 = 𝐼𝐷. Do đây chỉ là đèn LED tín hiệu nên không cần quá sáng, vì vậy chúng ta có thể giảm dòng điện lại để tiết kiệm điện năng hơn. Thông thường thì để đèn LED ở khoảng 10 – 15 mA. Trong ví dụ này chọn 15 mA. ◮ Giá trị điện trở: R = 𝑈𝑅 / 𝐼𝑅 = 5V / 15mA = 200Ω. ◮ Vì trên thị trường điện trở 200Ω không thông dụng, mà điện trở 220Ω lại thông dụng hơn nên chọn giá trị điện trở 220Ω. Module Relay Chức năng: dùng để bật tắt các thiết bị có công suất lớn/điện áp lớn/dòng điện lớn. Sơ đồ chân: ◮ Hiện nay, trên thị trường có rất nhiều loại module relay khác nhau với sơ đồ nguyên lý khác nhau (cách ly hoặc không cách ly/tích cực mức thấp hoặc cao), điện áp hoạt động khác nhau (5V, 12V hoặc 24V) nên khi sử dụng ta cần chú ý đọc kỹ hướng dẫn trước khi sử dụng. ◮ Thông thường, module sẽ có 2 domino (hoặc 1 header và 1 domino) để điều khiển module. Một domino để cấp nguồn (sẽ có 2 chân DC+ và DC- hoặc GND và VCC) và tín hiệu điều khiển bật tắt relay (IN). Domino còn lại dùng để điều khiển các thiết bị công suất với 3 ngõ COM (cổng chung), NO (normal open – thường mở) và NC (normal close – thường đóng). Nếu relay được kích (hoạt động) thì COM sẽ nối với NO, ngược lại nếu relay không được kích thì COM sẽ nối với NC. Hình 3.3: Cách kết nối với Arduino Mô phỏng Link: https://www.tinkercad.com/ things/lc8CaKsOHyI Cách lập trình: tương tự như lập trình với LED. 1 void setup() 2 { 3 pinMode(13, OUTPUT); 4 } 5 6 void loop()
  • 40. 3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 33 Hình 3.4: Cách kết nối với Arduino (mô phỏng) 7 { 8 digitalWrite(13, HIGH); 9 delay(1000); // Wait for 1000 millisecond(s) 10 digitalWrite(13, LOW); 11 delay(1000); // Wait for 1000 millisecond(s) 12 } Nút nhấn (button) Chức năng: dùng để chọn chế độ hoạt động, nhận một tín hiệu điều khiển từ người dùng mà vi điều khiển có thể hiểu được. Sơ đồ chân: nút nhấn được chia thành hai loại cơ bản là push button và switch. ◮ Push button: sau khi tác động lực thì nút nhấn quay về trạng thái ban đầu, trạng thái đóng/mở có thể được giữ hoặc không giữ. Hình 3.5: Nút nhấn push-button. Nguồn từ Wikimedia và Wikimedia. ◮ Switch: sau khi tác động lực để thay đổi trạng thái thì nút nhấn không quay về trạng thái ban đầu, trạng thái của nút nhấn được giữ khi ngừng tác dụng lực Hình 3.6: DIP switch. Nguồn từ Wiki- media.
  • 41. 3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 34 Hình 3.7: Nút nhấn tích cực thấp Hình 3.8: Sơ đồ mạch điện của nút nhấn (tích cực thấp) Cách lập trình: chúng ta cùng xem xét hai mạch đọc nút nhấn khác nhau. Link: https://www.tinkercad.com/ things/3IN5fVNqBmI Theo sơ đồ nguyên lý như hình 3.8: ◮ Xét trạng thái tại chân D2, giả sử chân D2 chỉ nhận mức tín hiệu điện áp và không có dòng điện đi vào chân D2. ◮ Nếu S1 không được nhấn thì điện áp tại chân D2 là 5V. 𝐼𝑅1 = 0𝐴 vì đã giả sử không có dòng điện vào chân D2. 𝑈𝐷2 = 5𝑉 − 𝐼𝑅1 𝑅1 = 5𝑉 − 0(𝐴).10(𝑘Ω) = 5𝑉 ◮ Ngược lại, nếu S1 được nhấn thì chân D2 được nối với 0V nên điện áp đọc vào là 0V. Trạng thái nút nhấn Giá trị điện áp Mức logic Không nhấn 5V HIGH Nhấn 0V LOW 1 int buttonState = 0; 2 3 void setup() 4 { 5 // Set pin 2 as INPUT 6 pinMode(2, INPUT);
  • 42. 3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 35 7 // Set pin 13 as OUTPUT 8 pinMode(13, OUTPUT); 9 } 10 11 void loop() 12 { 13 // Read the state of the pushbutton value 14 buttonState = digitalRead(2); 15 // Check if pushbutton is pressed. if it is, the 16 // buttonState is LOW 17 if (buttonState == LOW) { 18 // Turn LED on 19 digitalWrite(13, HIGH); 20 } else { 21 // Turn LED off 22 digitalWrite(13, LOW); 23 } 24 delay(10); // Delay a little bit to improve simulation performance 25 } Hình 3.9: Nút nhấn tích cực ca Link: https://www.tinkercad.com/ things/9tb62dzleah Ta xét mạch điện thứ 2 như hình 3.10, ta có sơ đồ nguyên lý như sau: ◮ Tương tự, ta cũng xét trạng thái tại chân D2. ◮ Nếu S1 không được nhấn thì điện áp tại chân D2 là 0V. 𝐼𝑅1=0A vì đã giả sử không có dòng điện vào chân D2. 𝑈𝐷2 = 𝐼𝑅1 𝑅1 = 0(𝐴).10(𝑘Ω) = 0𝑉 ◮ Ngược lại, nếu S1 được nhấn thì chân D2 được nối với 5V nên điện áp đọc vào là 5V. Trạng thái nút nhấn Giá trị điện áp Mức logic Không nhấn 0V LOW Nhấn 5V HIGH 1 int buttonState = 0; 2 3 void setup() 4 { 5 // Set pin 2 as INPUT 6 pinMode(2, INPUT); 7 // Set pin 13 as OUTPUT 8 pinMode(13, OUTPUT); 9 }
  • 43. 3 GENERAL PURPOSE INPUT/OUTPUT – GPIO 36 Hình 3.10: Sơ đồ mạch điện của nút nhấn (tích cực cao) 10 11 void loop() 12 { 13 // Read the state of the pushbutton value 14 buttonState = digitalRead(2); 15 // Check if pushbutton is pressed. if it is, the 16 // buttonState is HIGH 17 if (buttonState == HIGH) { 18 // Turn LED on 19 digitalWrite(13, HIGH); 20 } else { 21 // Turn LED off 22 digitalWrite(13, LOW); 23 } 24 delay(10); // Delay a little bit to improve simulation performance 25 }
  • 44. TIME 4 4.1 Giới thiệu . . . . . . . . . . . 37 4.2 Một số hàm thường dùng . 37 delay() . . . . . . . . . . . . . 37 delayMicroseconds() . . . . 38 millis() . . . . . . . . . . . . . 38 micros() . . . . . . . . . . . . 39 4.3 Ghi chú . . . . . . . . . . . . 39 4.1 Giới thiệu Time (thời gian) trong lập trình vi điều khiển khá quan trọng vì giúp cho vi điều khiển nhận biết thời gian cần phải thực hiện/thời gian đã thực hiện một nhiệm vụ do người lập trình đưa ra. 4.2 Một số hàm thường dùng delay() Chức năng: dùng để dừng chương trình trong khoảng thời gian xác định (đơn vị: milliseconds). Cú pháp: delay(ms); ◮ ms: số milliseconds cần dừng chương trình (kiểu dữ liệu: un- signed long Kết quả trả về: không. Ví dụ: 1 delay(1000); // wait for a second Lưu ý: trong thời gian thực hiện hàm delay thì vi điều khiển không thực hiện bất cứ chương trình nào khác như đọc giá trị cảm biến, tính toán hay thao tác trên các chân. Tuy nhiên, hàm delay sẽ không vô hiệu các ngắt (interrupt) nên các giao tiếp bằng phần cứng vẫn hoạt động (đường nhận dữ liệu), phát xung PWM hay trạng thái các Pin không thay đổi. Vì vậy, chúng ta nên thật sự lưu ý khi dùng các hàm delay. Ngoài ra, hàm delay cũng được dùng như một cách chống rung phím bằng phần mềm.
  • 45. 4 TIME 38 delayMicroseconds() Chức năng: dùng để dừng chương trình trong khoảng thời gian xác định (đơn vị: microseconds). Cú pháp: delayMicroseconds(us); ◮ us: số microseconds cần dừng chương trình (kiểu dữ liệu: un- signed long) Kết quả trả về: không. Ví dụ: 1 delayMicroseconds(1000); // wait for a millisecond Lưu ý: hàm này làm việc chính xác khi us > 3 microsenconds. millis() Chức năng: trả về số milliseconds kể từ khi bắt đầu hoạt động. Giá trị này sẽ bị tràn và quay về 0 khi đạt giá trị tối đa (khoảng 50 ngày). Cú pháp: time = millis(); Kết quả trả về: số milliseconds (kiểu dữ liệu: unsigned long). Ví dụ: 1 unsigned long time; 2 3 void loop() { 4 time = millis(); 5 delay(1000); // wait a second 6 } Lưu ý: kiểu dữ liệu là unsigned long nên khi ta thực hiện phép toán thì phải đảm bảo chung kiểu dữ liệu để không gây ra lỗi sai về giá trị tối đa của các kiểu dữ liệu.
  • 46. 4 TIME 39 micros() Chức năng: trả về số microseconds kể từ khi bắt đầu hoạt động. Giá trị này sẽ bị tràn và quay về 0 khi đạt giá trị tối đa (khoảng 70 phút). Cú pháp: time = micros(); Kết quả trả về: số microseconds (kiểu dữ liệu: unsigned long). Ví dụ: 1 unsigned long time; 2 3 void loop() { 4 time = micros(); 5 delay(1000); // wait a second 6 } 4.3 Ghi chú Khi sử dụng các hàm delay thì ta cần phải cân nhắc có nên sử dụng hay không vì nó sẽ làm ảnh hưởng đến tốc độ chương trình. Ta cũng có thể sử dụng các phương pháp khác để thay thế các hàm delay như tính toán thời gian dựa vào việc lấy các hàm giá trị thời gian thực.
  • 47. UART 5 5.1 Giới thiệu . . . . . . . . . . . 40 5.2 Một số hàm thường dùng . 40 Serial.begin() . . . . . . . . . 40 Serial.end() . . . . . . . . . . 41 Serial.print()/Serial.println() 41 Serial.write() . . . . . . . . . 42 Serial.availableForWrite() . 43 Serial.available() . . . . . . . 43 Serial.read() . . . . . . . . . . 44 Một số hàm khác . . . . . . . 44 Lưu ý . . . . . . . . . . . . . . 45 5.3 Một số module mẫu . . . . . 45 Module bluetooth HC-05 . . 45 Giao tiếp giữa 2 Arduino . . 47 5.4 Lời kết . . . . . . . . . . . . . 49 5.1 Giới thiệu UART là một chuẩn giao tiếp nối tiếp để giao tiếp giữa các thiết bị với nhau. (Lý thuyết về UART sẽ được giải thích ở quyển 2) Trong Arduino, UART được dùng để giao tiếp giữa board Arduino với các module khác có cùng giao thức hoặc giữa board Arduino với các board vi điều khiển khác. Giao thức UART gồm 2 dây tín hiệu là TX và RX. Khi 2 board muốn giao tiếp với nhau thì phải mắc chéo (nghĩa là TX board này sẽ nối với RX board kia, RX board này nối với TX board kia). Một thông số quan trọng là tốc độ truyền (baudrate), các thông số này là những số được quy định trước 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200,... Một số thông số liên quan đến giao tiếp UART: baudrate, số stop bit (1/2), số bytes dữ liệu (5/6/7/8(/9)), kiểm tra lỗi parity (odd- /even/none). 5.2 Một số hàm thường dùng Serial.begin() Chức năng: thiết lập baudrate (và các thông số khác) cho giao tiếp nối tiếp. Đây là thiết lập baudrate cho module phần cứng (chân 0 và 1 trên vi điều khiển). Thông số khác như số lượng data, parity và stop bits (giá trị mặc định là 8 data bits, no parity và 1 stop bit). Cú pháp: Serial.begin(speed); Serial.begin(speed, config); ◮ Serial: đối tượng cổng nối tiếp (serial port) ◮ speed: baudrate (bits per second – kiểu dữ liệu: long) ◮ config: SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1 (giá trị mặc định), SERIAL_5N2, SERIAL_6N2, SERIAL_7N2, SE- RIAL_8N2, SERIAL_5E1, SERIAL_6E1, SERIAL_7E1, SERIAL_- 8E1, SERIAL_5E2, SERIAL_6E2, SERIAL_7E2, SERIAL_8E2, SE- RIAL_5O1, SERIAL_6O1, SERIAL_7O1, SERIAL_8O1, SERIAL_- 5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2
  • 48. 5 UART 41 Kết quả trả về: không. Ví dụ: void setup() { // opens serial port, sets data rate to 9600 bps Serial.begin(9600); } void loop() {} Serial.end() Chức năng: ngừng giao tiếp nối tiếp. Cú pháp: Serial.end(); Kết quả trả về: không. Serial.print()/Serial.println() Chức năng: in dữ liệu ra thông qua cổng nối tiếp với các ký tự nhìn thấy được (human-readble ASCII text). Với lệnh Serial.println() thì sau dữ liệu in ra thì có kèm theo 2 ký tự: ký tự trả về đầu dòng (a carriage return character - ASCII 13 – ’r’) và ký tự xuống hàng (a newline character – ASCII 10 – ’n’). Cú pháp: Serial.print(val); Serial.print(val, format); ◮ Serial: đối tượng cổng nối tiếp ◮ val: giá trị cần in ra (với bất kỳ kiểu dữ liệu nào) ◮ format: định dạng dữ liệu của val (xem ví dụ) Kết quả trả về: số bytes dữ liệu được ghi ra cổng nối tiếp (kiểu dữ liệu: size_t). Ví dụ: Link: https://www.tinkercad.com/ things/6L8YcnI8993
  • 49. 5 UART 42 1 void setup() { 2 Serial.begin(9600); // open the serial port at 9600 bps: 3 } 4 5 void loop() { 6 Serial.println("--------------------------"); 7 Serial.print(78); // gives "78" 8 Serial.println(); // enter new line 9 Serial.print(1.23456); // gives "1.23" 10 Serial.println(); // enter new line 11 Serial.print('N'); // gives "N" 12 Serial.println(); // enter new line 13 Serial.print("Hello world."); // gives "Hello world." 14 Serial.println(); // enter new line 15 16 Serial.println(78, BIN); // gives "1001110" 17 Serial.println(78, OCT); // gives "116" 18 Serial.println(78, DEC); // gives "78" 19 Serial.println(78, HEX); // gives "4E" 20 Serial.println(1.23456, 0); // gives "1" 21 Serial.println(1.23456, 2); // gives "1.23" 22 Serial.println(1.23456, 4); // gives "1.2346" 23 delay (1000); // wait a sencond 24 } Serial.write() Chức năng: ghi dữ liệu nhị phân (binary data) thông qua cổng nối tiếp. Dữ liệu này được gửi dạng byte hoặc chuỗi các bytes. Cú pháp: Serial.write(val); Serial.write(str); Serial.write(buf, len); ◮ Serial: đối tượng cổng nối tiếp ◮ val: giá trị cần gửi đi – 1 byte ◮ str: chuỗi các ký tự gửi đi – chuỗi các bytes ◮ buf: mảng các byte cần gửi ◮ len: chiều dài chuỗi dữ liệu cần gửi Kết quả trả về: số bytes dữ liệu được ghi ra cổng nối tiếp (kiểu dữ liệu: size_t). Ví dụ: 1 void setup() { 2 Serial.begin(9600); // open the serial port at 9600 bps: 3 } 4 5 void loop() {
  • 50. 5 UART 43 6 Serial.write(45); // send a byte with the value 45 7 8 int bytesSent = Serial.write(“hello”); /* Send the string “hello” and return the length of the string.*/ 9 delay (1000); // wait a sencond 10 } Lưu ý: nếu bộ đệm (buffer) đủ khoảng trống để truyền thì hàm Se- rial.write() sẽ trả về trước khi tất cả các ký tự được gửi, các ký tự gửi đi sẽ được lưu trữ trong bộ đệm. Nếu độ đệm truyền bị đầy thì hàm này sẽ khóa (block) mãi cho đến khi đủ khoảng trống cho việc truyền. Để tránh trường hợp bị khóa (block) thì nên kiểm tra trước số byte còn trống trong bộ đệm bằng hàm Serial.availableForWrite(). Serial.availableForWrite() Chức năng: số bytes còn lại trong bộ đệm truyền. Cú pháp: Serial.availableForWrite(); ◮ Serial: đối tượng cổng nối tiếp Kết quả trả về: số bytes có sẵn để ghi. Serial.available() Chức năng: số bytes có sẵn trong bộ đệm nhận (bộ đệm tối đa là 64 bytes). Cú pháp: Serial.available(); ◮ Serial: đối tượng cổng nối tiếp Kết quả trả về: số bytes có sẵn để đọc. Ví dụ: 1 void setup() { 2 Serial.begin(9600); // open the serial port at 9600 bps: 3 } 4 5 void loop() { 6 // reply only when you receive data: 7 if (Serial.available() > 0) { 8 // Read data at here. Please see next example. 9 } 10 }
  • 51. 5 UART 44 Serial.read() Chức năng: đọc một byte dữ liệu đến. Cú pháp: Serial. read(); ◮ Serial: đối tượng cổng nối tiếp Kết quả trả về: ký tự đầu tiên trong chuỗi dữ liệu nhận được (kiểu dữ liệu: int). Ví dụ: 1 int incomingByte = 0; // for incoming serial data 2 3 void setup() { 4 // opens serial port, sets data rate to 9600 bps 5 Serial.begin(9600); 6 } 7 8 void loop() { 9 // send data only when you receive data: 10 if (Serial.available() > 0) { 11 // read the incoming byte: 12 incomingByte = Serial.read(); 13 14 // say what you got: 15 Serial.print("I received: "); 16 Serial.println(incomingByte, DEC); 17 } 18 } Một số hàm khác Ngoài các hàm trên, Serial vẫn còn nhiều hàm khác sử dụng cho những trường hợp khác ◮ if(Serial): kiểm tra cổng nối tiếp có sẵn sàng chưa. ◮ Serial.find(): tìm xem trong bộ đệm có ký tự muốn tìm hay không bằng cách đọc dữ liệu trong bộ đệm đến khi tìm được ký tự mong muốn. ◮ Serial.findUntil(): tìm chuỗi ký tự cần tìm có tồn tại trong bộ đệm hay không bằng cách đọc dữ liệu trong bộ đệm đến khi tìm được chuỗi mục tiêu hoặc chuỗi để kết thúc sự tìm kiếm. ◮ Serial.flush(): đợi cho việc truyền hoàn tất cho tất cả các ký tự trong bộ đệm.
  • 52. 5 UART 45 ◮ Serial.parseFloat(): trả về giá trị có kiểu dấu chấm động phù hợp đầu tiên trong bộ đệm. Các ký tự không phải số hoặc dấu trừ sẽ bị bỏ qua. Hàm này sẽ chấm dứt khi gặp ký tự đầu tiên không phải dấu chấm động hoặc hết thời gian (thời gian này được thiết lập bởi hàm Serial.setTimeout()). ◮ Serial.parseInt(): tương tự như hàm Serial.parseFloat() nhưng giá trị cần đọc là kiểu int. ◮ Serial.peek(): trả về ký tự tiếp theo trong bộ đệm mà không có xóa nó đi ra khỏi bộ đệm. ◮ Serial.readBytes(): đọc về số byte dữ liệu được xác định trước. ◮ Serial.readBytesUntil(): đọc về số byte dữ liệu được xác định trước và phát hiện ký tự kết thúc hoặc hết thời gian định trước. ◮ Serial.readString(): đọc dữ liệu trong bộ đệm dưới kiểu dữ liệu là String. ◮ Serial.readStringUntil(): đọc dữ liệu trong bộ đệm dưới kiểu dữ liệu là String cho đến khi ký tự kết thúc được phát hiện hoặc hết thời gian định trước. ◮ Serial.setTimeout(): thiết lập thời gian tối đa cho việc đợi dữ liệu từ cổng nối tiếp. Giá trị mặc định là 1 giây. ◮ serialEvent(): hàm này được gọi khi có dữ liệu đến Để biết thêm thông tin chi tiết, vui lòng truy cập vào link này. Lưu ý Khi lập trình C/C++, chúng ta cần phân biệt ký tự ‘a’ và chuỗi “a”. Trường hợp ‘a’ (dấu nháy đơn) thì đây là ký tự a có kích thước một byte. Trường hợp “a” (dấu nháy kép) thì đây là chuỗi ký tự có kích thước hai bytes vì cuối chuỗi phải có chứa 1 byte kết thúc chuỗi ‘0’. Khi bạn nhập ‘ab’, compiler có thể ngầm hiểu đây là chuỗi mà không báo lỗi, nếu compiler không hỗ trợ thì sẽ thông báo lỗi. ◮ ’r’ (CR – Cariage Return): di chuyển con trỏ về đầu hàng mà không xuống dòng. ◮ ’n’ (LF – Line Feed): di chuyển con trỏ xuống hàng tiếp theo. 5.3 Một số module mẫu Module bluetooth HC-05 Hình 5.1: Module bluetooth HC-05. Nguồn từ Eprolabs
  • 53. 5 UART 46 Chức năng: là module chuyển đổi chuẩn giao tiếp nối tiếp sang blue- tooth Giao tiếp bằng bluetooth là giao tiếp tầm gần (dưới 10m). . Khi máy tính kết nối module này thì máy tính nhận là một cổng COM ảo. Sơ đồ chân: Hình 5.2: Kết nối module bluetooth HC- 05 với Arduino. ◮ Kết nối như hình 5.2, ta có thể không cần thiết phải nối chân KEY với 3.3V. ◮ Thông số mặc định của bluetooth là HC-05, mật khẩu kết nối là 1234 và baudrate mặc định là 9600. ◮ Chúng ta có thể thay đổi cấu hình của module bluetooh bằng chế độ AT command, baudrate cho chế độ này là 38400. Bạn có thể tìm kiếm trên mạng bằng từ khóa “hc05 at commands”. ◮ Lưu ý, TX và RX phải được mắc chéo. Cách lập trình: 1 int incomingByte = 0; // for incoming serial data 2 3 void setup() { 4 Serial.begin(9600); // opens serial port, sets data rate to 9600 bps 5 } 6 7 void loop() { 8 Serial.println("minht57 lab"); 9 // send data only when you receive data: 10 if (Serial.available() > 0) { 11 // read the incoming byte: 12 incomingByte = Serial.read(); 13 14 // say what you got: 15 Serial.print("I received: "); 16 Serial.println(incomingByte, DEC); 17 } 18 delay(1000); 19 } Lưu ý: đây là chuẩn bluetooth 2.0 nên chỉ giao tiếp 1 – 1 (tức là một master và 1 slave giao tiếp với nhau). Nếu bạn muốn tìm hiểu về nhiều module giao tiếp với nhau có thể tham khảo với từ khóa “module bluetooth 4.0” hoặc “module bluetooth BLE”. Thông tin thêm:
  • 54. 5 UART 47 ◮ Để hai thiết bị có thể giao tiếp được với nhau thì cần phải có dây kết nối giữa chúng. Trong trường hợp cần sự linh hoạt và không cần dây kết nối rờm rà thì cần phải có một giao thức không dây để truyền tải dữ liệu như bluetooth, wifi,. . . Vì việc kết nối với phần cứng để truyền nhận tín hiệu không dây thường phức tạp, trải qua nhiều bước cấu hình nên các nhà sản xuất đã chế tạo các module bluetooth/wifi và giao tiếp với các module này thông qua các chuẩn giao tiếp phổ biến trên vi điều khiển như UART/SPI. ◮ Giao tiếp với các module này thường thông qua tập lệnh "AT command". AT (ATtention) command là bộ lệnh hướng dẫn khi sử dụng để giao tiếp với module. Ví dụ khi làm việc với module HC-05, chúng ta muốn thay đổi tên bluetooth thì công việc cần làm là: vào chế độ AT command → gửi chuỗi "AT+NAME=AHIHI_- DONGOC". Giao tiếp giữa 2 Arduino Ví dụ 1: Link: https://www.tinkercad.com/ things/kSSCq6eIkV7 board Arduino 1 sẽ gửi một chuỗi ký tự sang board Arduino 2. Khi board Arduino 2 nhận được sẽ in ra ngoài. Hình 5.3: Giao tiếp nối tiếp giữa 2 board Arduino Chương trình Arduino 1 1 void setup() { 2 Serial.begin(9600); // opens serial port, sets data rate to 9600 bps 3 } 4 5 void loop() { 6 Serial.println("minht57 lab, Vietnam"); 7 delay(1000); 8 } Chương trình Arduino 2
  • 55. 5 UART 48 1 char incomingByte = 0; // for incoming serial data 2 3 void setup() { 4 Serial.begin(9600); // opens serial port, sets data rate to 9600 bps 5 } 6 7 void loop() { 8 // send data only when you receive data: 9 while (Serial.available() > 0) { 10 // read the incoming byte: 11 incomingByte = Serial.read(); 12 Serial.print(incomingByte); 13 } 14 } Ví dụ 2: Link: https://www.tinkercad.com/ things/lxCfz5KzuRK board Arduino 1 sẽ đọc tín hiệu nút nhấn và gửi trạng thái của nút nhấn sang board Arduino 2. Board Arduino 2 sẽ bật/tắt đèn LED theo trạng thái nút nhấn nhận được từ board Arduino 1. Hình 5.4: Giao tiếp nối tiếp giữa 2 Ar- duino Chương trình Arduino 1 1 int buttonState = 0; 2 3 void setup() { 4 // Set pin 2 as INPUT 5 pinMode(2, INPUT); 6 // Open serial port, sets data rate to 9600 bps 7 Serial.begin(9600); 8 } 9 10 void loop() { 11 // Read the state of the pushbutton value 12 buttonState = digitalRead(2); 13 // Check if pushbutton is pressed. If it is, the buttonState is HIGH 14 if (buttonState == HIGH) { 15 Serial.println('1');
  • 56. 5 UART 49 16 } 17 else 18 { 19 Serial.println('2'); 20 } 21 22 delay(10); // Delay a little bit to improve simulation performance 23 } Chương trình Arduino 2 1 char string; 2 3 void setup() { 4 // Set pin 13 as OUTPUT 5 pinMode(13, OUTPUT); 6 // Opens serial port, sets data rate to 9600 bps 7 Serial.begin(9600); 8 } 9 10 void loop() { 11 /* 12 // send data only when you receive data: 13 while (Serial.available() > 0) { 14 // read the incoming byte: 15 incomingByte = Serial.read(); 16 17 }*/ 18 string = Serial.read(); 19 if (string == '1') { 20 // Turn LED on 21 digitalWrite(13, HIGH); 22 } 23 else if (string == '2') { 24 // Turn LED off 25 digitalWrite(13, LOW); 26 } 27 } 5.4 Lời kết UART (hay giao tiếp nối tiếp) là một chuẩn giao tiếp rất phổ biến và thông dụng trong lập trình vi điều khiển vì tính đơn giản để sử dụng truyền dữ liệu nhỏ. Chúng ta nên phân biệt rằng giao tiếp nối có rất nhiều chuẩn và UART là một trong số đó nên cẩn thận khi nói các khái niệm: ◮ UART là giao tiếp nối tiếp (đúng). ◮ Giao tiếp nối tiếp chỉ là UART (sai).
  • 57. 5 UART 50 Có một chuẩn giao tiếp cũng khá phổ biến trong công nghiệp là RS232/RS485, vì mức điện áp hoạt động của 2 chuẩn giao tiếp là khác nhau nên khi vi điều khiển giao tiếp với các module có chuẩn giao tiếp này thì chúng ta cần thêm một module chuyển đổi từ UART sang RS232/RS485. Lưu ý rằng đối tượng Serial là đối tượng hoạt động với ngoại vi phần cứng bên trong chip (chân 0 và chân 1) và một số board Arduino chỉ có 1 module giao tiếp nối tiếp, nếu ứng dụng của chúng ta muốn hoạt động nhiều module giao tiếp nối tiếp hơn thì có các bạn có thể tham khảo thư viện “SoftwareSerial”10 10: Các chức năng cơ bản của hai thư viện Serial và SoftwareSerial là giống nhau. . Khi bạn sử dụng thư viện này thì không có các chức năng liên quan đến phần cứng như interrupt hay event.
  • 58. ANALOG 6 6.1 Giới thiệu . . . . . . . . . . . 51 6.2 Một số hàm thường dùng . 52 analogRead() . . . . . . . . . 52 analogReference() . . . . . . 53 analogWrite() . . . . . . . . . 54 6.3 Một số module mẫu . . . . . 55 Biến trở . . . . . . . . . . . . . 55 Module điều khiển động cơ L298N . . . . . . . . . . . . . . . 56 6.4 Lời kết . . . . . . . . . . . . . 62 6.1 Giới thiệu Tín hiệu số (digital signal) là tín hiệu mà được thể hiện bằng một chuỗi con số rời rạc. Tại bất kỳ thời điểm nào, nó chỉ có một giá trị trong tập giá trị hữu hạn của nó. Các mạch điên tử số thông dụng mà chúng ta thường thấy thì có 2 mức điện áp 0V và VCC. Người ta gọi là tín hiệu nhị phân (binary signal). Hình 6.1: Tín hiệu số (digital). Nguồn từ Wikimedia. Tín hiệu tương tự (analog signal) là tín hiệu có giá trị thay đổi liên tục theo thời gian. Hình 6.2: Tín hiệu tương tự (analog). Nguồn từ Wikimedia. Như chúng ta đã biết, vi điều khiển chỉ có thể xử lý bằng tín hiệu số. Nhưng các tín hiệu trong môi trường đều là tín hiệu tương tự và vi điều khiển muốn hiểu giá trị của các tín hiệu này thì phải có module chuyển đổi từ tín hiệu tương tự sang tín hiệu số. Người ta đã nghiên cứu ra một số phương pháp chuyển từ tín hiệu tương tự sang tín hiệu số được gọi chung là ADC (Analo-Digital Converter). Ngược lại khi muốn chuyển từ tín hiệu số sang tín hiệu tương tự thì có module DAC (Digital-Analog Converter). Ngoài hai module ADC và DAC, module điều chế độ rộng xung (PWM - Pulse Width Modulation) cũng được Arduino hỗ trợ như một tính năng analog dùng để xuất xung với độ rộng xung thay đổi trong một chu kỳ xác định trước.
  • 59. 6 ANALOG 52 Hình 6.3: Xung PWM. Điện áp trung bình được tín theo công thức 𝑉 = 𝑇𝑂𝑁 𝑇𝐶𝑦𝑐𝑙𝑒 𝑉𝐶𝐶 . Một số ứng dụng phổ biến của PWM có thể kể đến như: điều khiền động cơ (vi trí, tốc độ, moment), điều khiển điện áp (buck/boost/. . . ), thay đổi độ sáng của đèn,. . . Lưu ý: phân biệt điện áp trung bình và dạng tín hiệu của xung PWM, Ví dụ khi PWM với 𝑇𝑂𝑁 =50% thì điện áp trung bình là 0.5𝑉𝐶𝐶 (khi lấy VOM đo), nhưng dạng tín hiệu thực tế là dạng xung có 2 mức điện áp là 0V và 𝑉𝐶𝐶 (dùng oscilloscope). 6.2 Một số hàm thường dùng analogRead() Chức năng: đọc giá trị analog từ một chân được chỉ định trước. Board Arduino có chứa nhiều kênh analog (mỗi nguồn tín hiệu là một kênh). ◮ Tín hiệu trả về có độ phân giải 10-bit, có nghĩa là vi điều khiển đọc điện áp từ 0V – VCC thì giá trị trả về từ 0 đến 1023 (210 − 1). Nếu tín hiệu điện áp nhỏ hơn 0V thì giá trị đọc được vẫn là 0, tương tự nếu điện áp lớn VCC thì giá trị trả về là 1023. ◮ Giá trị ngưỡng điện áp tham chiếu có thể thay đổi khi sử dụng hàm analogReference(). ◮ Các board Arduino như UNO, Nano, Mini, Mega thì thời gian đọc tín hiệu analog ngõ vào khoảng 100 us.
  • 60. 6 ANALOG 53 Board Operating voltage Usable pins Max resolution Uno 5V A0..A5 10 bits Mini, Nano 5V A0..A7 10 bits Mega, Mega2560 5V A0..A14 10 bits Micro 5V A0..A11 10 bits Leonardo 5V A0..A11 10 bits Zero 3.3V A0..A5 12 bits Due 3.3V A0..A11 12 bits Cú pháp: analogRead(pin); ◮ pin: chân analog muốn đọc. Kết quả trả về: giá trị analog đọc được. Ví dụ: 1 /* potentiometer wiper (middle terminal) connected to analog pin 3 outside 2 leads to ground and +5V */ 3 int analogPin = A3; 4 // variable to store the value read 5 int val = 0; 6 7 void setup() { 8 Serial.begin(9600); // setup serial 9 } 10 void loop() { 11 val = analogRead(analogPin); // read the input pin 12 Serial.println(val); // debug value 13 } Lưu ý: nếu chân analog không được kết nối với bất kỳ nguồn tín hiệu nào thì giá trị trả về sẽ bị dao động không biết trước. analogReference() Chức năng: cấu hình điện áp tham chiếu cho chân analog ngõ vào. Cú pháp: analogReference(type); ◮ type: loại tham chiếu được sử dụng. • Arduino AVR Boards (Uno, Mega, etc.) * DEFAULT: điện áp 5V nếu dùng board Arduino 5V hoặc điện áp 3.3V nếu dùng board Arduino 3.3V. * INTERNAL: điện áp tham chiếu nội, bằng 1.1V trên chip ATmega168 or ATmega328P và 2.56V trên chip ATmega8 (không có sẵn trên board Arduino Mega).
  • 61. 6 ANALOG 54 * INTERNAL1V1: điện áp tham chiếu nội 1.1V (chỉ có trên board Arduino Mega). * INTERNAL2V56: điện áp tham chiếu nội 2.56V (chỉ có trên board Arduino Mega). * EXTERNAL: sử dụng điện áp trên chân AREF để tham chiếu ( từ 0 đến 5V). • Arduino SAMD Boards (Zero, etc.) * AR_DEFAULT: điện áp tham chiếu 3.3V * AR_INTERNAL: điện áp tham chiếu nội 2.23V * AR_INTERNAL1V0: điện áp tham chiếu nội 1.0V * AR_INTERNAL1V65: điện áp tham chiếu nội 1.65V * AR_INTERNAL2V23: điện áp tham chiếu nội 2.23V * AR_EXTERNAL: sử dụng điện áp trên chân AREF để tham chiếu. • Arduino SAM Boards (Due) * AR_DEFAULT: giá trị mặc định là 3.3V, đây là điện áp tham chiếu duy nhất trên board Arduino Due. Kết quả trả về: không. Lưu ý: ◮ Sau thay đổi điện áp tham chiếu, vài giá trị trả về đầu tiên sẽ không chính xác. ◮ Không sử dụng điện áp tham chiếu ngoài (thông qua chân AREF) với mức điện áp nhỏ hơn 0V hay lớn hơn 5V. ◮ Nếu sử dụng điện áp tham chiếu ngoài, chúng ta cần thiết lập điện áp tham chiếu là EXTERNAL trước khi gọi analogRead(). Bằng không thì sẽ gây ngắn mạch điện áp tham chiếu nội và điện áp tham chiếu ngoại gây ra “hư hỏng” vi điều khiển. analogWrite() Chức năng: ghi ra dạng sóng PWM đến chân được chỉ định. ◮ Sau khi gọi hàm analogWrite(), chân được chỉ định sẽ tạo ra sóng PWM với duty cycle được chỉ định mãi tới khi gọi hàm analogWrite() lần nữa. Tần số PWM ở hầu hết các chân là 490Hz, riêng chân 5 và 6 trên Uno (hoặc các board tương tự) có tần số xấp xỉ 980Hz. Cú pháp: analogWrite(pin, value); ◮ pin: chân để ghi xung PWM. ◮ value: duty cycle, giá trị từ 0 đến 255. Kết quả trả về: không. Ví dụ: thiết lập độ sáng đèn tương ứng với giá trị biến trở.
  • 62. 6 ANALOG 55 1 // LED connected to digital pin 9 2 int ledPin = 9; 3 // potentiometer connected to analog pin 3 4 int analogPin = 3; 5 // variable to store the read value 6 int val = 0; 7 8 void setup() { 9 pinMode(ledPin, OUTPUT); // sets the pin as output 10 } 11 12 void loop() { 13 val = analogRead(analogPin); // read the input pin 14 /* analogRead values go from 0 to 1023, analogWrite values from 0 to 255 */ 15 analogWrite(ledPin, val / 4); 16 } 6.3 Một số module mẫu Biến trở Hình 6.4: Biến trở xoay. Nguồn từ Wiki- media và electronics-tutorials. Sơ đồ chân: ◮ Biến trở có 3 chân (như hình 6.4) với giá trị là R, giá trị điện trở 𝑅12 và 𝑅23 sẽ thay đổi giá trị khi xoay núm vặn. 𝑅12 + 𝑅23 = 𝑅 ◮ Nếu ta nối chân 1 và 3 của biến trở vào nguồn điện 5V, ta có mạch chia áp với giá trị điện áp tại chân số 2 thay đổi từ 0V đến 5V. Link: https://www.tinkercad.com/ things/8jh2P36bep4 Hình 6.5: Arduino và biến trở
  • 63. 6 ANALOG 56 Cách lập trình: 1 int sensorValue = 0; 2 float voltage = 0; 3 4 void setup() 5 { 6 pinMode(A0, INPUT); 7 Serial.begin(9600); 8 } 9 10 void loop() 11 { 12 // Reads the input on analog pin 0: 13 sensorValue = analogRead(A0); 14 // Prints out the value you read: 15 Serial.println(sensorValue); 16 17 // Calculates voltages 18 voltage = sensorValue * 5.0 / 1023; 19 Serial.print("Volt (V): "); 20 Serial.println(voltage, 2); 21 Serial.println("------------"); 22 23 delay(10); // Delay a little bit to improve simulation performance 24 } Lưu ý: ◮ Biến trở có nhiều hình dạng khác nhau (như hình 6.6) và cách hoạt động khác nhau nhưng mục đích chung vẫn là thay đổi giá trị điện trở. ◮ Biến trở có nhiều giá trị điện trở tối đa khác nhau nên tùy vào ứng dụng mà ta chọn giá trị điện trở phù hợp. Hình 6.6: Biến trở các loại. Nguồn từ Wikimedia. Module điều khiển động cơ L298N 12 12: Bài viết lấy nguồn từ howtomecha- tronics Chức năng: ◮ Để điều khiển tốc độ động cơ (chạy nhanh hay chậm) thì chúng ta cần thay đổi giá trị điện áp cho động cơ và động cơ là một thiết bị tiêu thụ điện với điện áp cao lẫn dòng điện cao nên việc vi điều khiển điều khiển trực tiếp động cơ là không thể. Để hỗ trợ vấn đề này, chúng ta cần một mạch lái (mạch điều khiển –
  • 64. 6 ANALOG 57 driver) động cơ để nhận tín hiệu điều khiển từ vi điều khiển và cho ngõ ra điện áp tương ứng. ◮ Mạch điều khiển động cơ L298N là một module thông dụng điều khiển 2 động cơ DC 12V riêng biệt. Hình 6.7: Module điều khiển động cơ L298N. Nguồn từ howtomechatronics. Sơ đồ chân: ◮ Hai domino MotorA và MotorB được nối với động cơ. ◮ Chân 12V-GND để cấp nguồn cho động cơ. ◮ Chân 5V-GND để cấp nguồn cho mạch điều khiển động cơ. Chúng ta có thể không cấp nguồn 5V thì có thể dùng jumper để lấy điện áp từ nguồn 12V. Hình 6.8: Chi tiết các chân module. ◮ Các chân ENA, IN1, IN2 để điều khiển MotorA, tương tự các chân ENB, IN3, IN4 để điều khiển MotorB. ◮ Các chân INx để điều khiển chiều quay của động cơ, chân ENx để cấp xung thay đổi tốc độ động cơ. Hình 6.9: Chiều của dòng điện khi IC L298N hoạt động. Cách lập trình: ◮ Dùng biến trở để thay đổi tốc độ động cơ: 1 /* Arduino DC Motor Control - PWM | H-Bridge | L298N - Example 01 2 3 by Dejan Nedelkovski, www.HowToMechatronics.com 4 */