1
Mục lục
Mở đầu 2
1 Giới thiệu vi xử lý AVR 3
2 Một số phần mềm, thiết bị hỗ trợ cho việc lập trình AVR 5
2.1 Phần mềm CodeVision 5
2.2 Phần mềm mô phỏng Proteus 5
2.3 Mạch nạp và phần mềm nạp 6
3 Ứng dụng của vi xử lý ATmega16 7
3.1 Điều khiển I/O (In/Out) 7
3.2 Ngắt ngoài (Interrupt) 8
3.3 Timer/Counter 9
3.4 Điều khiển độ rộng xung – PWM 11
3.4.1 Khái niệm độ rộng xung 11
3.4.2 Tạo PWM bằng ATmega16 11
3.5 Chuyển đổi ADC 14
3.6 Giao tiếp USART 16
3.6.1 Các thuật ngữ được dùng trong giao tiếp USART 16
3.6.2 Giao tiếp USART trên ATmega16 17
4 Các bài tập ví dụ 19
4.1 Lập trình điều khiển thiết bị ngoài (Led, động cơ…) 19
4.1.1 Phân tích mạch điện 19
4.1.2 Xây dựng nguyên lý điều khiển 19
4.1.3 Mô phỏng trên Proteus 21
4.1.4 Lập trình cho vi xử lý 22
4.1.5 Chạy thử trên mạch mô phỏng 29
4.2 Lập trình ngắt ngoài (INT) 31
4.2.1 Phân tích mạch điện 31
4.2.2 Nguyên lý điều khiển và xây dựng mạch mô phỏng trên Proteus 31
4.2.3 Lập trình cho vi xử lý 32
4.3 Lập trình Timer/Counter 35
4.3.1 Phân tích, xây dựng nguyên lý điều khiển và mạch mô phỏng 35
4.3.2 Lập trình cho vi xử lý 35
4.4 Điều khiển độ rộng xung PWM 42
4.4.1 Phân tích mạch điện 42
4.4.2 Nguyên lý điều khiển 42
4.4.3 Xây dựng mạch mô phỏng trên Proteus 43
4.4.4 Lập trình cho vi xử lý 43
4.5 Chuyển đổi ADC 46
4.5.1 Phân tích mạch điện 46
4.5.2 Nguyên lý điều khiển và mạch mô phỏng trên Proteus 47
4.5.3 Lập trình cho vi xử lý 49
4.6 Truyền nhận dữ liệu với máy tính 58
4.6.1 Phân tích mạch điện 58
4.6.2 Nguyên lý điều khiển và mạch mô phỏng 58
4.6.3 Lập trình cho vi xử lý 59
Phụ lục 64
2
Mở đầu
Hiện nay các vi xử lý, hay vi điều khiển (MCU – Micro Controller Unit) đang được sử
dụng rộng rãi trong nhiều kĩnh vực như: điều khiển, tự động hóa, đo đạc, truyền thông… So
với các phương pháp điều khiển, đo đạc truyền thống (cơ khí, điện tử tương tự…), thì sử
dụng vi xử lý có các ưu điểm như: nhỏ gọn, ít tốn năng lượng, thời gian đáp ứng nhanh, có
thể lập trình được…
Vi xử lý họ AVR của hãng ATMEL hiện đang được sử dụng tương đối phổ biến. Vi xử
lý họ AVR là các vi xử lý lập trình được. Sau khi được lập trình, vi xử lý sẽ hoạt động theo
thuật toán được viết trong lập trình của người dùng. Như vậy người dùng có thể lập trình cho
vi điều khiển hoạt động theo ý đồ của mình để giải quyết một các bài toán (điều khiển, đo
đạc…).
Trong lập trình cho vi điều khiển, thường sử dụng ngôn ngữ lập trình C và có liên quan
tới cấu trúc của vi xử lý, để người đọc chưa được học về ngôn ngữ lập trình C hoặc cấu trúc
vi xử lý có thể dễ dàng theo dõi, những phần đó sẽ được diễn giải theo cách càng đơn giản
càng tốt. Như vậy người đọc có thể theo dõi tài liệu mà không thấy quá khó hiểu, tuy nhiên
cũng vì thế nên các kiến thức được trình bày không đầy đủ và chỉ ở mức cơ bản nhất, để
người đọc có thể nhanh chóng tự lập trình được một số ứng dụng cơ bản với vi điều khiển
AVR.
3
1 Giới thiệu vi xử lý AVR
AVR là tên gọi chung các vi xử lý của hãng ATMEL. Hãng ATMEL sản xuất các vi xử
lý với các chức năng, khả năng tính toán khác nhau, được đặt tên khác nhau: ATtiny25,
ATtiny48…,ATmega8, ATmega16, ATmega128…
Các vi xử lý khác nhau thì khác nhau về dung lượng bộ nhớ ROM, RAM, các khối chức
năng, số lượng chân… Trong tài liệu này, các hướng dẫn và ví dụ được viết cho vi xử lý
ATmega16. Sơ đồ chân, các khối chức năng và cách sử dụng, đặc tính làm việc… của vi lý
được nhà sản xuất ghi trong file “datasheet” kèm theo, có thể tìm trên internet.
Hình 1: Vi xử lý ATmega16
Các vi xử lý được lập trình bằng ngôn ngữ lập trình (trong tài liệu này sử dụng ngôn ngữ
lập trình C) nhờ các phần mềm (AVRStudio, CodeVision…).
Các vi xử lý đều có chung một số đặc tính điện khi làm việc như sau:
Điện áp làm việc là +5V.
Dòng điện qua các chân của vi xử lý nhỏ (cỡ vài chục mA), nên không thể đấu
trực tiếp vi điều khiển với các thiết bị công suất lớn (động cơ…).
Vi xử lý Atmega 16 có 2 kiểu đóng gói khác nhau, loại đóng gói như hình sau là loại
thường, phổ biến nhất, 40 chân theo sơ đồ:
Hình 2:Sơ đồ chân của Atmega16
Tất cả các chân của ATmega16 được đánh số thứ tự từ 1 đến 40 như hình vẽ, chiều của
vi xử lý được lấy theo đầu lõm hoặc chấm tròn trên vi xử lý.
Khi làm việc, các chân của vi điều khiển có hai mức trạng thái là 0 và 1. Trạng thái 0
ứng với mức điện áp 0 V còn trạng thái 1 ứng với mức điện áp +5V.
4
Chức năng của các chân trên vi xử lý ATmega16 như sau:
Chân 10 – VCC: Là chân cấp nguồn +5V cho vi xử lý.
Chân 11 và 31 – GND: Là hai chân nối với nguồn âm. Khi thiết kế mạch, phải
nối cả hai chân 11 và 31 với nguồn âm.
Chân 9 – RESET: Bình thường chân 9 ở trạng thái 0, khi cấp điện +5V vào chân
RESET, trạng thái của nó từ 0 lên 1, và reset lại toàn bộ hoạt động của vi xử lý,
tương đương với việc ngắt nguồn nuôi vi xử lý rồi cấp nguồn lại.
Chân 12 – XTAL1 và 13 – XTAL2: Là hai chân nối với thạch anh. Thạch anh là
một linh kiện có tác dụng tạo dao động điện, vi xử lý sẽ hoạt động theo các dao
động này (ví dụ 4Mhz, 12 Mhz…). Trong một số trường hợp có thể dùng thạch
anh được gắn sẵn trong vi xử lý, hai chân XTAL1 và XTAL2 được bỏ trống.
Chân 30 – AVCC: Là chân cấp nguồn cho khối chuyển đổi ADC (Analog to
Digital Converter). ACD là một chức năng của vi xử lý, cho phép đo điện áp đặt
tại chân 33, 34, …, 40 của vi xử lý. Xem thêm mục Chuyển đổi ADC.
Chân 32 – AREF: Điện áp tham chiếu. Chân AREF được sử dụng khi cần dùng
chức năng ADC của vi điều khiển. Khi cần dùng chức năng ADC, người dùng
cần đặt vào chân AREF một điện áp, gọi là điện áp tham chiếu. Xem thêm mục
Sử dụng ADC.
Các chân 1 … 8: Được nhóm chung thành một “cổng”, gọi là PORT B, từng chân
trong Port B được ký hiệu PB0, PB1, …, PB7 ứng với các chân từ 1 đến 8. Các
chân này có thể đảm nhiệm các chức năng khác nhau, tùy vào thiết lập của người
dùng khi lập trình.
Các chân 14 … 21: Được nhóm chung thành một “cổng”, gọi là PORT D, từng
chân trong Port D được ký hiệu PD0, PD1, …, PD7 ứng với các chân từ 14 đến
21. Các chân này có thể đảm nhiệm các chức năng khác nhau, tùy vào thiết lập
của người dùng khi lập trình.
Các chân 22 … 29: Được nhóm chung thành một “cổng”, gọi là PORT C, từng
chân trong Port C được ký hiệu PC0, PC1, …, PC7 ứng với các chân từ 22 đến
29. Các chân này có thể đảm nhiệm các chức năng khác nhau, tùy vào thiết lập
của người dùng khi lập trình.
Các chân 33 … 40: Được nhóm chung thành một “cổng”, gọi là PORT A, từng
chân trong Port A được ký hiệu PA0, PA1, …, PA7 ứng với các chân từ 1 đến 8.
Các chân này có thể đảm nhiệm các chức năng khác nhau, tùy vào thiết lập của
người dùng khi lập trình.
Tóm tắt 1
Vi xử lý ATmega16:
Là vi xử lý lập trình được.
Điện áp làm việc +5V, dòng nhỏ.
Có 4 port là Port A, Port B, Port C, Port D. Mỗi port có 8 chân, có thể đảm
nhiệm các chức năng khác nhau.
5
2 Một số phần mềm, thiết bị hỗ trợ cho việc lập trình AVR
2.1 Phần mềm CodeVision
Phần mềm CodeVision là phần mềm hỗ trợ lập trình cho vi xử lý AVR bằng ngôn ngữ
C. Cách sử dụng cụ thể CodeVision được viết trong các ví dụ trong tài liệu.
Hình 3: Giao diện CodeVision
2.2 Phần mềm mô phỏng Proteus
Hình 4: Giao diện Proteus
Proteus là phần mềm mô phỏng mạch điện trên máy tính, có giao diện trực quan, với các
linh kiện điện tử được sắp xếp trên màn hình và được nối dây, đặt các thông số để có thể chạy
thử mạch điện trên máy tính.
Proteus có sẵn rất nhiều các linh kiện điện tử có thể tìm thấy trên thị trường, bao gồm cả
các vi xử lý AVR. Các lệnh theo ngôn ngữ C sau khi được viết bằng CodeVision có thể được
đưa vào Proteus để chạy vi xử lý AVR tương ứng. Dùng Proteus ta có thể kiểm tra xem lập
trình viết trong CodeVision đã đúng hay chưa, như vậy không cần thiết phải thử trên mạch
thật, tiết kiệm thời gian, tiền bạc.
Tuy nhiên do là mô phỏng trên máy tính, nên một số phần quan trọng trong mạch thực
(nguồn điện, sự tương thích về công suất giữa các phần tử…) không được tính đến, dẫn tới
mạch điện mô phỏng chỉ có giá trị tham khảo, kiểm tra nguyên lý hoạt động trước khi tiến
hành làm mạch thật.
6
2.3 Mạch nạp và phần mềm nạp
Sau khi thử trên Proteus, và làm một mạch điện thật bên ngoài có vi xử lý AVR, để cho
vi xử lý AVR chạy được những gì đã lập trình, phải nạp chương trình cho nó (gần giống như
cài phần mềm cho máy tính), muốn làm điều đó chúng ta cần mạch nạp và phần mềm nạp.
Mạch nạp là một mạch điện nối giữa vi xử lý và máy tính, còn phầm mềm nạp được cài
trên máy tính để điều khiển mạch nạp. Sau khi nạp, chương trình chúng ta viết bằng
AVRStudio sẽ được nhớ trong bộ nhớ của vi xử lý, và vi xử lý sẽ hoạt động theo như chương
trình đó.
Tóm tắt 2
Lập trình bằng ngôn ngữ C cho vi điều khiển AVR bằng phần mềm
CodeVision hoặc AVRStudio.
Chạy mô phỏng vi xử lý trên máy tính để kiểm tra bằng phần mềm Proteus.
Nạp chương trình cho vi xử lý bằng mạch nạp và phần mềm Khazama.
7
3 Ứng dụng của vi xử lý ATmega16
Các chức năng cơ bản của vi xử lý ATmega16 sẽ được trình bày trong tài liệu gồm có:
Chức năng I/O (In/Out).
Ngắt ngoài (INT – INTerrupt).
Đếm thời gian (Timer/Counter).
Điều khiển độ rộng xung (PWM – Pulse Width Module).
Chuyển đổi ADC (Analog to Digital Converter).
Truyền nhận dữ liệu với máy tính (USART – Universal Synchronous and
Asynchronous serial Receiver and Transmitter).
Còn các chức năng khác của ATmega16 như SPI, … sẽ không được trình bày ở đây.
3.1 Điều khiển I/O (In/Out)
Điều khiển I/O, hay còn gọi là điều khiển Vào/Ra, là một chức năng cơ bản nhất của vi
xử lý. Chức năng I/O cho phép người lập trình gán trạng thái 0 hoặc 1 (ứng với mức điện áp
0V và +5V tương ứng) cho một hoặc nhiều chân trong các Port A, B, C hoặc D. Nói cách
khác, người lập trình có thể điều khiển điện áp tại một chân bất kỳ trong số 4 port ở mức 0V
hoặc +5V.
Với chức năng In/Out, vi xử lý có thể điều khiển bật/tắt trực tiếp hoặc gián tiếp các thiết
bị ngoài như đèn Led, động cơ điện…
Nếu muốn sử dụng một chân nào đó trong số các port A, B, C hoặc D (chẳng hạn như
chân PB6), khi lập trình người dùng phải thiết lập chức năng I/O cho chân PB6. Cách thiết
lập sẽ được trình bày trong ví dụ Lập trình điều khiển thiết bị ngoài.
Tóm tắt 3.1
Dùng chức năng I/O điều khiển trạng thái các chân vi xử lý.
8
3.2 Ngắt ngoài (Interrupt)
Ngắt (Interrupt) trong AVR, bao gồm cả ngắt ngoài, là một tín hiệu khẩn cấp gửi đến bộ
xử lý, yêu cầu bộ xử lý dừng các công việc hiện tại (A) để thực hiện một việc nào đó (B). Sau
khi khi kết thúc B, bộ xử lý sẽ quay về làm tiếp việc A đang dang dở. Tín hiệu khẩn cấp đó
có thể đến từ bên trong vi xử lý (từ bộ Timer, USART…) hoặc từ bên ngoài (nhấn một nút
bấm…).
Ngắt ngoài là loại ngắt duy nhất độc lập với các thiết bị của vi xử lý, vì tín hiệu ngắt đến
từ bên ngoài, ví dụ như khi người dùng bấm một nút bấm ở bên ngoài…
Trên Atmega16 có ba bộ ngắt ngoài, ứng với các chân 16 (PD2), 17 (PD3) và chân 3
(PB2), ký hiệu lần lượt là INT0, INT1 và INT2.
Hình sau thể hiện một tín hiệu ngắt tạo ra bằng một button (nút bấm) tới ngắt INT0.
Hình 5: Tín hiệu ngắt
Với mạch điện button như hình trên, khi chưa nhấn nút, điện áp đặt trên chân INT0 gần
bằng +5V. Khi nhấn nút, chân INT0 được nối với GND nên điện áp chân INT0 lúc này là 0V.
Sau khi nhả nút, điện áp chân INT0 lại trở về +5V.
Quá trình điện áp chân INT0 giảm nhanh tử gần +5V xuống 0V gọi là Falling Edge
(cạnh xuống), còn quá trình điện áp tăng từ 0V lên +5V gọi là Rising Edge (cạnh lên). Người
dùng có thể lập trình cho ngắt INT0 hoạt động khi xảy ra Falling Edge, Rising Edge hoặc cả
hai.
Nếu chọn Falling Edge, ngắt xảy ra khi vừa nhấn xong nút.
Nếu chọn Rising Edge, ngắt xảy ra khi nhả xong nút.
Nếu chọn cả Falling Edge và Rising Edge, ngắt sẽ xảy ra 2 lần khi vừa nhấn xong
nút và sau khỉ nhả xong nút.
Cách kích hoạt chức năng ngắt ngoài INT0, INT1 hoặc INT2 và cách lựa chọn cạnh
xuống/cạnh lên được hướng dẫn trong ví dụ Lập trình ngắt ngoài.
Ngắt ngoài thường dùng để thực hiện giao tiếp giữa người và vi xử lý (thông qua thao
tác nhấn nút), hoặc có thể dùng để đếm sự kiện bên ngoài (ví dụ như số sản phẩm đi qua cảm
biến)…
Tóm tắt 3.2
ATmega16 có 3 ngắt.
Ngắt theo cạnh xuống, cạnh lên hoặc cả hai.
9
3.3 Timer/Counter
Timer/Counter, hay còn gọi là bộ đếm thời gian, bộ định thời… có chức năng đếm, hoặc
định ra một khoảng thời gian và cả đếm sự kiện. Trên một số vi xử lý, timer/counter còn có
thêm chức năng điều chỉnh độ rộng xung (PWM). Trên ATmega16 có 3 bộ timer/counter,
gọi tắt là T/C0, T/C1,T/C2.
Các bộ T/C hoạt động bằng cách đếm số xung dao động của thạch anh nuôi vi xử lý theo
tỉ lệ (Prescale) 1:1 hoặc 1:8, Thạch anh, như đã nói ở phần 1, tạo ra dao động để vi xử lý
hoạt động. Mỗi linh kiện thạch anh có một tần số dao động nhất định (ví dụ 4 Mhz,
12Mhz…). Mỗi chu kỳ dao động ứng với một khoảng thời gian Δt nhỏ, bằng cách đếm các
dao động này, các bộ T/C có thể định ra các khoảng thời gian khác nhau. Ví dụ với thạch anh
1Mhz, thì chu kỳ dao động là:
6
6
1
10 ( ) 1( )
1.10
t s s
Nếu muốn xác định một khoảng thời gian
1 s 1000ms
, bộ T/C sẽ phải dùng một biến
đếm để đếm đủ 1000 lần. Các biến đếm đó được đặt tên là TCNT0, TCNT1 hoặc TCNT2 tùy
theo bộ T/C0, T/C1, T/C2 tương ứng.
Các biến đếm TCNT được lưu trong vi xử lý bằng 8 hoặc 16 bit (bit là các ô nhớ trong vi
xử lý và chỉ có hai trạng thái 0 hoặc 1, tổ hợp của các bit này sẽ tương ứng với giá trị của
biến TCNT theo hệ nhị phân). Thường dùng tên gọi T/C 8 bit hoặc T/C 16 bit chính là nói tới
số bit dùng để lưu giá trị của biến TCNT.
Vì số bit bị giới hạn, nên khả năng đếm của biến TCNT bị giới hạn (với 8 bit, có thể đếm
tối đa từ 0 đến 255), dẫn tới khoảng thời gian xác định được nếu đếm trực tiếp số dao dộng
của thạnh anh cũng bị giới hạn.
Giải pháp đưa ra là đếm theo tỉ lệ (prescale) đã nói ở trên. Thay vì đếm mỗi dao động
một lần, nếu đếm theo tỉ lệ, ví dụ 1:8, thì cứ 8 dao động của thạnh anh, T/C mới đếm một lần,
tức là giá trị TCNT tăng thêm 1.
Hình 6: Đếm theo tỉ lệ 1:1 và 1:8
Như vậy với ví dụ ở trên, để định được khoảng thời gian 1ms, bộ T/C chỉ cần đếm:
1000
125
8
(lần)
Lúc này biến đếm TCNT chỉ phải chạy, ví dụ như từ giá trị ban đầu 131 tới giá trị tối đa
255 (đủ 125 lần đếm). Khi biến TCNT tới giá trị 255, thì sẽ được reset về giá trị ban đầu để
đếm tiếp, đồng thời xảy ra ngắt. Do vậy tín hiệu ngắt của T/C là khi biến TCNT tới giá trị cực
đại, và ngắt có T/C tạo ra gọi là ngắt trong.
10
Cách chọn tỉ lệ, giá trị ban đầu, giá trị tối đa và các thiết lập liên quan khác của T/C sẽ
được trình bày trong ví dụ Lập trình Timer/Counter.
Tóm tắt 3.3
Timer/Counter (T/C) tạo ngắt trong theo các quãng thời gian định trước.
ATmega16 có 2 T/C 8 bit và 1 T/C 16 bit.
Biến đếm TCNT và tỉ lệ Prescale.
11
3.4 Điều khiển độ rộng xung – PWM
3.4.1 Khái niệm độ rộng xung
Để hiểu khái niệm độ rộng xung, ta xét mạch điện đơn giản sau đây:
Hình 7: Mạch điện ví dụ độ rộng xung
Một động cơ được mắc nối tiếp với một nút bấm như hình vẽ. Nếu ta bấm nút, động cơ
sẽ chạy, nhả nút, động cơ sẽ dừng. Điện áp giữa hai đầu động cơ có dạng xung như trên hình
7. Nếu nhấn, nhả nút bấm theo chu kỳ, thì điện áp hai đầu động cơ cũng thay đổi theo chu kỳ,
lúc này sẽ có khái niệm Time Period và Duty Cycle. Duty Cycle là thời gian điện áp ở mức
+5V, còn Time Period là chu kỳ của xung, bao gồm Duty Cycle và thời gian điện áp bằng 0V
còn lại.
Nếu giảm Duty Cycle, thì động cơ chạy chậm lại. Ngược lại, nếu tăng Duty Cycle thì
động cơ chạy nhanh lên. Duty Cycle giảm tối thiểu bằng 0 và tăng tối đa bằng Time Period.
Điều chỉnh độ rộng xung chính là điều chỉnh Duty Cycle, và đôi khi là cả Time Period.
3.4.2 Tạo PWM bằng ATmega16
Vì điều chỉnh độ rộng xung là điều chỉnh thời gian Time Period và thời gian Duty Cycle,
vì vậy có thể dùng T/C để tạo PWM.
Vi xử lý ATmega16 có thể tạo cùng lúc 4 tín hiệu PWM, gọi là 4 kênh PWM tại các
chân như hình sau:
Hình 8: Vị trí các chân có thể dùng để tạo tín hiệu PWM
Trong đó PWM0 do T/C0 tạo ra, PWM1A và PWM1B dó T/C1 tạo ra, PWM2 do T/C2
tạo ra. Muốn tạo PWM nào thì ta cần phải thiết lập T/C tương ứng.
12
Để dễ hiểu hãy lấy ví dụ muốn tạo tín hiệu PWM tại chân PD4 (PWM1B), ta phải dùng
T/C1 và thiết lập cho T/C1 ở chế độ tạo PWM. T/C1 là timer/counter 16 bit, do đó biến
TCNT1 có thể đếm tới giá trị MAX là 65535.
Ý tưởng ở đây là biến TCNT1 chạy từ 0 tới giá trị OCR1B, sau đó chạy tiếp tới giá trị
ICR1. Trong đó OCR1B là một giá trị do người dùng đặt trước ứng với khoảng thời gian
Duty Cycle. Giá trị ICR1 cũng là một giá trị do người dùng đặt trước nhưng ứng với Time
Period. Như vậy khi biến TCNT1 chạy từ 0 qua OCR1B tới ICR1, điện áp trên chân OC1B sẽ
thay đổi theo Duty Cycle và Time Period tương ứng.
T/C1 có 3 chế độ hoạt động để tạo PWM là:
Fast PWM (PWM tần số cao).
Phase correct PWM (PWM với pha chính xác).
Phase correct and frequency correct PWM (PWM với pha và tần số chính xác).
Ba chế độ trên khác nhau ở cách tính Duty Cycle và sai số điều khiển. Trong tài liệu này
sẽ giới thiệu hai chế độ thông dụng nhất là Fast PWM.
Fast PWM:
Hoạt động của T/C1 ở chế độ tạo Fast PWM có thể được biểu diễn qua đồ thị dưới:
Hình 9: Chế độ Fast PWM
Max là giá trị cực đại mà biến TCNT1 có thể đếm được, với T/C1 là Timer/Counter 16
bit nên Max = 65535. Khi thiết lập các giá trị cho OCR1B và ICR1 không được vượt quá giá
trị Max này.
Ở chế độ Fast PWM biến TCNT1 đếm xung thạch anh theo tỉ lệ Prescale định trước,
chạy từ 0 tới OCR1B, lúc này điện áp trên chân OC1B là +5V. Khi TCNT1 chạy từ OCR1B
tới ICR1, điện áp chân OC1B là 0V. Đồng thời khi TCNT1 chạm mốc ICR1, biến TCNT1
được reset về 0 để tiếp tục đếm cho chu kỳ tiếp theo.
Như vậy thời gian biến TCNT1 chạy từ 0 tới OCR1B ứng với Duty Cycle, thời gian biến
TCNT1 chạy từ 0 tới ICR1 ứng với Time Period.
13
Chẳng hạn muốn tạo một xung PWM có Time Period là 2ms, Duty Cycle là 1 ms với
thạch anh 4 Mhz, tỉ lệ Prescale = 8, ta có:
Mỗi dao động thạch anh ứng với khoảng thời gian là:
63
6
1
0,25.10 ( ) 0,25.10 ( s)
4.10
sm
Mỗi lần đếm của biến TCNT1 ứng với khoảng thời gian là:
33
8. 8.0,25.10 2.10 ( )ms
Suy ra để tạo được Time Period (chu kỳ) của PWM là 2 ms, ta phải cho biến
TCNT1 đếm 1000 lần, từ 0 tới 1000, tức là giá trị ICR1 = 1000.
Tương tự, để tạo được Duty Cycle của PWM là 1 ms, thì giá trị OCR1B = 500.
Thông thường khi lập trình, người ta cố định giá trị ICR1 ngay từ đầu (Time Period cố
định), còn giá trị OCR1B được thay đổi khi vi xử lý hoạt động, nhằm tạo ra PWM có Duty
Cycle thay đổi.
Cách kích hoạt chế độ Fast PWM, chọn các giá trị OCR1B, ICR1 và các thông số khác
sẽ được trình bày trong ví dụ Điều khiển độ rộng xung PWM.
Tóm tắt 3.4
PWM tạo ra do bộ T/C, ATmega16 có 4 kênh PWM (tại chân OC0;
OC1A; OC1B; OC2).
PWM dùng điều chỉnh tốc độ động cơ, động cơ bước và động cơ servo.
Tạo PWM nhờ biến TCNT và các giá trị OCR và ICR. Điều chỉnh độ rộng
xung bằng cách điều chỉnh giá trị OCR.
14
3.5 Chuyển đổi ADC
Trong các ứng dụng đo lường và điều khiển bằng vi xử lý, bộ chuyển đổi tương tự-số
(ADC) là một thành phần rất quan trọng. Các đại lượng vật lý như nhiệt độ, áp suất, dòng
điện,… đều có thể dùng cảm biến và các mạch xử lý để chuyển về dạng tín hiệu điện áp. Trên
vi xử lý ATmega16, có thể đo 8 điện áp khác nhau đặt trên các 8 chân thuộc PORT A, gọi là
8 kênh ADC, được ký hiệu là ADC0, …, ADC7. Điện áp cấp cho bộ chuyển đổi ADC là +5V
và được cấp qua chân AVCC.
Hình 10: 8 kênh ADC của ATmega16
Điện áp đặt tại một trong số các kênh ADC, ví dụ ADC0, sẽ được bộ chuyển đổi ADC
của ATmega16 đo và trả lại một giá trị tương ứng. Để hiểu rõ hơn về hoạt động của ADC ta
cần hiểu một số khái niệm sau:
Điện áp tham chiếu - V
ref
: Là giá trị lớn nhất mà bộ ADC có thể đo được. Trong đo đạc
truyền thống, với các thiết bị đo, chúng ta cũng gặp khái niệm “giá trị lớn nhất mà thiết bị có
thể đo được”. Ví dụ như một đồng hồ đo áp suất có thể đo được tối đa 70 bar, có nghĩa là
đồng hồ này chỉ dùng để đo áp suất nhỏ hơn 70 bar.
Với ADC trên ATmega16, V
ref
(điện áp tham chiếu) được người lập trình đặt trước khi
kích hoạt chức năng ADC. Có 3 cách chọn điện áp tham chiếu như sau:
Chọn điện áp so sánh bằng điện áp trên chân VREF.
Chọn điện áp so sánh bằng +5V, nối chân VREF với chân AVCC.
Chọn điện áp tham chiếu nội – V
ref
= 2,56 V. Điện áp tham chiếu nội này được
tạo ra bên trong vi xử lý, người dùng không cần cấp điện áp qua chân VREF.
Độ phân giải: Cho biết mức độ chính xác của phép đo bằng chuyển đổi ADC, cũng là số
bit dùng để chứa giá trị đầu ra của bộ chuyển đổi ADC. Chuyển đổi ADC trên ATmega16 là
bộ ADC 10 bit, giá trị bộ ADC trả về sau khi đo có giá trị từ 0 đến 1023, ứng với 1023
khoảng chia.
Hoạt động của ADC có thể giải thích đơn giản như sau, đầu tiên vi xử lý chia nhỏ điện
áp tham chiếu V
ref
làm 1023 khoảng, sau đó so sánh giá trị điện áp đặt tại chân đầu vào ADC
xem điện áp cần đo đó nằm ở khoảng bao nhiêu. Giá trị ADC đo được chính là số thứ tự của
khoảng điện áp tương ứng.
Hình 11: Hoạt động của ADC
15
Như hình trên, V
ref
được chia làm 1023 khoảng, từ 0 đến 1023. Điện áp cần đo V
in
đặt
vào chân ADC0, sau khi đo, bộ ADC sẽ trả về giá trị i, là khoảng điện áp ứng với V
in
. Như
vậy giá trị điện áp V
in
cần đo là:
ef
1023
in r
i
VV
Với độ phân giải không đổi, ta thấy nếu càng giảm V
ref
thì các khoảng điện áp càng thu
nhỏ, tức là ta đo được điện áp V
in
càng chính xác. Tuy nhiên V
ref
không được nhỏ hơn giá trị
lớn nhất của V
in
.
Các cách kích hoạt bộ chuyển đổi ADC, chọn kênh đo, điện áp tham chiếu và đo bằng
ADC sẽ được trình bày trong ví dụ Chuyển đôi ADC.
Tóm tắt 3.5
Dùng ADC để đo điện áp tại các kênh ADC. ATmega16 có 8 kênh ADC
với độ phân giải 10 bit.
Chọn V
ref
không quá lớn để phép đo được chính xác.
Điện áp cần đo:
16
3.6 Giao tiếp USART
USART được trình bày trong tài liệu này là một chuẩn truyền thông nối tiếp không đồng
bộ giữa các thiết bị digital. Chức năng USART trên vi xử lý ATmega16 là một trong số các
chức năng vi xử lý dùng để truyền/nhận dữ liệu với các thiết bị khác (máy tính, vi xử lý
khác…).
3.6.1 Các thuật ngữ được dùng trong giao tiếp USART
Truyền thông nối tiếp không đồng bộ: Trong nhiều bài toán cần sử dụng vi xử lý và
máy tính kết nối với nhau. Trong quá trình làm việc đó vi xử lý cần trao đổi dữ liệu với máy
tính, ví dụ như vi xử lý gửi giá trị đo đạc bằng ADC về máy tính và máy tính gửi tín hiệu điều
khiển xuống vi xử lý…
Giả sử dữ liệu cần trao đổi là các mã có chiều dài 8 bit, cách đơn giản nhất là kết nối 1
PORT (8 bit) của vi điều khiển với máy tính, mỗi dây nối sẽ chịu trách nhiệm truyền/nhận 1
bit dữ liệu. Đây gọi là cách giao tiếp song song, cách này là cách đơn giản nhất, nhưng nhược
điểm của cách truyền này là số đường truyền quá nhiều, giá trị càng lớn thì số đường truyền
cũng sẽ nhiều thêm.
Trong truyền thông nối tiếp dữ liệu được truyền từng bit trên 1 (hoặc một ít) đường
truyền, vì thế tiết kiệm đường truyền dữ liệu. Hình sau mô tả sự so sánh giữa 2 cách truyền
song song và nối tiếp trong việc truyền con số 187 thập phân (tức 10111011 nhị phân).
Hình 12: Phương pháp truyền song song (a) và nối tiếp (b)
Vì dữ liệu cần được “chia nhỏ” thành từng bit khi truyền/nhận, tốc độ truyền nối tiếp sẽ
bị giảm. Mặt khác, để đảm bảo tính chính xác của dữ liệu, bộ truyền và bộ nhận cần có những
“thỏa hiệp” hay những tiêu chuẩn nhất định.
Khái niệm “đồng bộ” để chỉ sự “báo trước” trong quá trình truyền. Lấy ví dụ thiết bị 1
(tb1) kết với với thiết bị 2 (tb2) bởi 2 đường, một đường dữ liệu và 1 đường xung nhịp. Cứ
mỗi lần tb1 muốn gửi 1 bit dữ liệu, tb1 điều khiển đường xung nhịp chuyển từ mức thấp lên
mức cao báo cho tb2 sẵn sàng nhận một bit. Bằng cách “báo trước” này tất cả các bit dữ liệu
có thể truyền/nhận dễ dàng với ít “rủi ro” trong quá trình truyền. Tuy nhiên, cách truyền này
đòi hỏi ít nhất 2 đường truyền cho 1 quá trình (gửi hoặc nhận dữ liệu).
Khác với cách truyền đồng bộ, truyền thông “không đồng bộ” chỉ cần một đường truyền
cho một quá trình. “Khung dữ liệu” đã được chuẩn hóa bởi các thiết bị nên không cần đường
xung nhịp báo trước dữ liệu đến. Ví dụ 2 thiết bị đang giao tiếp với nhau theo phương pháp
này, chúng đã được thỏa thuận với nhau rằng cứ 1ms thì sẽ có 1 bit dữ liệu truyền đến, như
thế thiết bị nhận chỉ cần kiểm tra và đọc đường truyền mỗi mili-giây để đọc các bit dữ liệu và
sau đó kết hợp chúng lại thành dữ liệu có ý nghĩa. Truyền thông nối tiếp không đồng bộ vì
thế hiệu quả hơn truyền thông đồng bộ (không cần nhiều đường truyền). Tuy nhiên, để quá
trình truyền thành công thì việc tuân thủ các tiêu chuẩn truyền là hết sức quan trọng.
Baud rate (tốc độ Baud): như trong ví dụ trên về việc truyền 1 bit trong 1ms, bạn thấy
rằng để việc truyền và nhận không đồng bộ xảy ra thành công thì các thiết bị tham gia phải
“thống nhất” nhau về khoảng thời dành cho 1 bit truyền, hay nói cách khác tốc độ truyền phải
17
được cài đặt như nhau trước, tốc độ này gọi là tốc độ Baud. Theo định nghĩa, tốc độ baud là
số bit truyền trong 1 giây. Ví dụ nếu tốc độ baud được đặt là 19200 thì thời gian dành cho 1
bit truyền là 1/19200 ~ 52.083us.
Frame (khung truyền): Khung truyền bao gồm các quy định về số bit trong mỗi lần
truyền, các bit “báo” như bit Start và bit Stop, các bit kiểm tra như Parity, ngoài ra số lượng
các bit trong một data cũng được quy định bởi khung truyền. Hình dưới là một ví dụ của một
khung truyền theo UART, khung truyền này được bắt đầu bằng một start bit, tiếp theo là 8
bit data, sau đó là 1 bit parity dùng kiểm tra dữ liệu và cuối cùng là 1 bits stop.
Hình 13: Khung truyền theo USART
Start: start là bit đầu tiên được truyền trong một frame truyền, bit này có chức năng báo
cho thiết bị nhận biết rằng có một gói dữ liệu sắp được truyền tới. Start là bit bắt buộc phải có
trong khung truyền.
Data: data hay dữ liệu cần truyền là thông tin chính mà chúng ta cần gửi và nhận.
Thường data gồm 8 bit.
Parity bit: parity là bit dùng kiểm tra dữ liệu truyền đúng không (một cách tương đối).
Parity bit không phải là bit bắt buộc và vì thế chúng ta có thể loại bit này khỏi khung truyền
(các ví dụ trong tài liệu này không dùng bit parity).
Stop bits: stop bits là một hoặc các bit báo cho thiết bị nhận rằng một gói dữ liệu đã
được gởi xong. Stop bits là các bits bắt buộc xuất hiện trong khung truyền.
3.6.2 Giao tiếp USART trên ATmega16
Vi điều khiển Atmega16 có 1 module truyền thông nối tiếp USART. Với chế độ truyền
nối tiếp không đồng bộ (cụ thể trong tài liệu này là giao tiếp với máy tính), chúng ta sử dụng
2 chân của Atmega16 để phục vụ cho truyền nhận dữ liệu là: chân truyền dữ liệu – TxD
(Transmitted Data) và chân nhận dữ liệu – RxD (Reveived Data).
Hình 14: Hai chân RXD và TXD dùng trong giao tiếp với máy tính
Mô-đun USART trên chip Atmega16 hoạt động theo chế độ quá trình truyền và nhận dữ
liệu có thể xảy ra đồng thời.
Khi sử dụng USART để giao tiếp với máy tính, phải thiết lập các thông số hoạt động ở vi
xử lý và trên máy tính giống nhau, bao gồm:
18
Baud rate: Baud rate càng cao, tốc độ truyền càng nhanh, tuy nhiên xác suất xảy
ra lỗi trong quá trình truyền/nhận cũng lớn hơn. Thường chọn Baud rate = 9600
với các ứng dụng không đòi hỏi tốc độ cao.
Frame: Khung truyền bao gồm start bit, data, parity bit và stop bit, trong đó start
bit là cố định và không thay đổi. Ví dụ trong tài liệu này sẽ lấy khung truyền có 8
bit data, không dùng parity bit và 1 bit stop.
USART của ATmega16 cũng có tính năng ngắt. Ngắt của USART xảy ra khi hoàn tất
gửi xong một dữ liệu, hoặc nhận xong một dữ liệu. Tính năng ngắt khi nhận xong một dữ liệu
sẽ được dùng trong ứng dụng truyền dữ liệu từ vi xử lý về máy tính.
Cách kích hoạt mô-đun USART, thiết lập các thông số Baud rate, Frame… và cách sử
dụng USART sẽ được trình bày trong ví dụ Giao tiếp USART.
Tóm tắt 3.6
Kết nối với máy tính dùng chức năng USART.
Baud rate = 9600; khung truyền có 8 bit data, 1 bit stop, không dùng
parity.
Ngắt khi truyền/nhận dữ liệu hoàn tất.
19
4 Các bài tập ví dụ
Tất cả các mạch điện sử dụng vi xử lý lập trình được đều được thiết kế theo trình tự:
Phân tích mạch: Yêu cầu, đặc điểm làm việc, …
Xây dựng nguyên lý điều khiển.
Vẽ mạch mô phỏng.
Lập trình cho vi xử lý.
Kiểm tra lập trình trên mạch mô phỏng, sửa lỗi lập trình hoặc mạch nguyên lý
nếu phát hiện lỗi.
Làm mạch thật. Kiểm tra lỗi trên mạch thật và tiến hành sửa hoặc làm lại mạch
nếu cần thiết.
Các bài tập ví dụ trong tài liệu này sẽ hướng dẫn người đọc chạy mô phỏng trên phần
mềm Proteus. Phần chạy thử trên mạch thật được thực hiện trên mạch đã được làm sẵn, sẽ có
sơ đồ nguyên lý và sơ đồ đi dây kèm theo.
4.1 Lập trình điều khiển thiết bị ngoài (Led, động cơ…)
Đây là ví dụ đầu tiên và đơn giản nhất trong các ví dụ ứng dụng vi xử lý, vì vậy sẽ được
viết kỹ từng bước và giải thích thật đơn giản.
Bài ví dụ “Lập trình điều khiển thiết bị ngoài” đang nói tới thực ra là điều khiển đóng-
ngắt các thiết bị ngoài (đèn Led, động cơ…) thay cho công-tắc điện, và là dạng điều khiển
đơn giản nhất trong các ứng dụng của vi điều khiển.
Mạch điện trong ví dụ này sẽ làm việc như sau: Điều khiển đèn Led sáng/tắt theo chu kỳ
0,5 giây.
4.1.1 Phân tích mạch điện
Với bất cứ một dự án (Project) thiết kế mạch điện nào, công việc đầu tiên cũng đều là
phân tích mạch điện cần thiết kế. Dựa vào yêu cầu và đặc điểm làm việc, chúng ta có thể xây
dựng được nguyên lý làm việc của các khối cơ bản của mạch (khối điều khiển, khối công
suất…) và lập trình cho vi xử lý.
Với ví dụ điều khiển Led như trên, yêu cầu và đặc điểm làm việc như sau:
Led sáng rồi tắt theo chu kỳ 0,5 giây.
Giữa các lần sáng/tắt của Led, không điều khiển thêm thiết bị nào khác.
Led có công suất bé (cỡ 0,1 W).
4.1.2 Xây dựng nguyên lý điều khiển
Như ở phần 1 đã nói, người dùng có thể lập trình để điều khiển điện áp tại một hay một
số chân của vi điều khiển. Có hai mức điện áp là 0V và +5V, và được gán với trạng thái 0 và
1 tương ứng. Ví dụ chân số 6 (PB5) của vi điều khiển ở trạng thái 1, tức là tại chân đó có điện
áp +5V, còn nếu ở trạng thái 0, tức là tại chân đó có điện áp bằng 0 (nối đất – GND).
Ý tưởng đưa ra là ta có thể dùng điện áp này để chạy một thiết bị nào đó nối trực tiếp với
chân của vi điều khiển. Tuy nhiên với điện áp +5V, đồng thời dòng điện cho phép nhỏ (vài
chục mA), nên ta chỉ có thể nối trực tiếp vào vi điều khiển những thiết bị có công suất rất
nhỏ, ví dụ như: đèn LED.
20
Hình 15: Nối trực tiếp vi xử lý với thiết bị có công suất nhỏ
Thực tế, để có thể điều khiển những thiết bị có công suất lớn hơn (hoặc làm cho Led
sáng hơn), người ta không nối trực tiếp vi điều khiển với thiết bị, mà qua một mạch công
suất. Có thể hiểu mạch công suất như một rơ-le trong mạch điện dân dụng.
Hình 16: Sơ đồ dùng điện thế 5V công suất nhỏ để điều khiển Mo-tơ 12V công suất lớn
Có nhiều linh kiện điện tử có thể thay thế cho một rơ-le, điển hình là các transistor. Hình
sau thể hiện nguyên lý hoạt động của mạch:
Hình 17: Vi xử lý điều khiển đóng/mở transistor để điều khiển tải
Một mạch tương tự như mạch transistor trình bày ở trên được gọi là mạch công suất, còn
khối vi xử lý được gọi là mạch điều khiển.
Bình thường khi chân 6 của vi điều khiển (nối với chân B của transistor) ở trạng thái 0
(0V), thì transistor đóng, tức là E và C không thông nhau.
Khi vi điều khiển chuyển trạng thái chân 6 sang 1 (+5V), transistor mở, tức là dòng điện
từ nguồn có thể chạy từ E sang C, qua tải rồi về âm.
Như vậy thay đổi trạng thái chân 6 sẽ điều khiển bật/tắt được Led.
21
4.1.3 Mô phỏng trên Proteus
Mô phỏng trên Proteus tức là xây dựng mạch nguyên lý và chạy thử nghiệm trên máy
tính bằng phần mềm Proteus.
Trình tự tiến hành mô phỏng mạch nguyên lý đóng/mở transistor bằng Proteus như sau:
Hình 18: Mở thư viện Proteus
Mở phần mềm Proteus, click vào các biểu tượng “1” và “2” để chọn lịnh kiện. Sau khi
click vào “2”, một của sổ mới sẽ hiện ra, cho phép chọn linh kiện.
Hình 19: Thư viện Proteus
Gõ tên linh kiện vào ô Keywords, ví dụ “mega16”, các linh kiện tìm được sẽ hiện bên ô
Result, click chuột để chọn linh kiên ATMEGA16 như trên hình 2.
Gõ tiếp “npn” để tìm linh kiện transistor loại PNP, gõ “led” để tìm linh kiện LED. Sau
đó sắp xếp và nối dây cho các linh kiện như trong hình 3.
22
Hình 20: Mạch mô phỏng ví dụ Điều khiển thiết bị ngoài
4.1.4 Lập trình cho vi xử lý
Lập trình chi vi xử lý AVR, cũng như với các vi xử lý khác, nên tiến hành theo các bước
như sau, để tạo thuận lợi trong việc xây dựng thuật toán, kiểm tra và sửa lỗi lập trình.
Xác định yêu cầu làm việc của vi xử lý.
Xây dựng thuật toán điều khiển.
Chuẩn bị lập trình và viết chương trình điều khiển.
Kiểm tra và sửa lỗi.
a) Xác định yêu cầu làm việc của vi xử lý
Trong ví dụ này, vi xử lý chỉ cần điều khiển trạng thái của chân số 6 của vi xử lý (có ký
hiệu PB5/MOSI trên Proteus) thay đổi giữ mức 0 (0V) và 1 (+5V) theo chu kỳ 0,5 giây.
b) Xây dựng thuật toán điều khiển.
Thuật toán điều khiển ở trong bài ví dụ này khá đơn giản, theo sơ đồ sau:
Hình 21: Sơ đồ thuật toán điều khiển vi xử lý
Sơ đồ được giải thích như sau:
Khi được cấp điện, vi xử lý chuẩn bị cho bước “Bắt đầu”, bao gồm thiết lập chân
PB5 (ứng với chân số 6 của vi xử lý) là chân điều khiển, có trạng thái ban đầu là
0 (Led tắt).
Sau đó vi xử lý làm việc theo chiều các mũi tên. Đầu tiên là đặt trạng thái chân
PB5 = 1 để Led sáng.
Đợi 0,5 s.
Đặt trạng thái PB5 = 0 để Led tắt.
Đợi 0,5 s.
Quay lại bước thứ 2 (PB5 = 1).
…….
Việc vi xử lý lặp đi lặp lại các bước như trên được gọi là “lặp”. Có nhiều cách để tạo
vòng lặp và điều khiển vòng lặp, sẽ được trình bày trong các ví dụ.
23
c) Chuẩn bị lập trình bằng phần mềm CodeVision.
Mở phần mềm CodeVision, nhấn Ctrl+N để tạo Project (dự án) mới, trong hộp thoại
“Create New File” chọn “Project” rồi nhấn “OK” “Yes”.
Hình 22: Tạo project mới
Hình 23: Sử dụng CodeWizardAVR
Hộp thoại “CodeWizardAVR” hiện ra, chọn tên chip là ATmega16, và chọn tần số hoạt
động của chip (ví dụ 4000000 Hz = 4Mhz).
Hình 24: Chọn vi xứ lý và tần số hoạt động
Hộp thoại “CodeWizardAVR còn có nhiểu mục khác, ứng với các chức năng của vi xử
lý như USART, ADC, External IRQ (ngắt) đã nói trong phần giới thiệu. Nhưng nếu chỉ cần
điều khiển trạng thái các chân của vi xử lý, ta chỉ cần vào mục Ports để quy định chức năng
vào/ra của chân vi xử lý.
24
Hình 25: Thiết lập các Port
Trên Proteus chúng ta muốn dùng chân số 6 của vi xử lý, có ký hiệu PB5, tức là chân
ứng với Bit 5 của Port B, vì vậy chúng ta set Bit 5 thành “Out” như trên hình.
Sau khi thiết lập ban đầu cho Atmega16 xong, click “File” “Generate, Save and Exit”.
Hình 26: Tạo và lưu project
Sau đó sẽ có 3 hộp thoại hiện ra để lưu các file cần thiết. Đặt tên và lưu các file đó vào
cùng một thư mục như trong hình.
25
Hình 27: Chọn thư mục lưu project
Sau khi lưu xong ta sẽ thấy một ô để viết các lệnh lập trình theo ngôn ngữ C. Các dòng
lệnh chúng ta viết sẽ được lưu trong file “bai1.c”.
Hình 28: Nơi viết các dòng lệnh
d) Viết chương trình điều khiển.
Khi mới tạo ra file bai1.c đã được ghi rất nhiều dòng lệnh, do phần mềm tự tạo ra theo
các thiết lập ở phần “CodeWizardAVR” chúng ta vừa làm. Tuy nhiên với bài này, chúng ta
chỉ cần theo tác ở một vài vị trí trong file “bai1.c”. Trình tự tiến hành được trình bày như sau:
Include các file header: Viết thêm dòng lệnh #include <delay.h> vào vị trí như
trong hình dưới. File delay.h là một file có sẵn của phần mềm Codevision, dùng khi muốn
delay (trì hoãn, tạm dừng) quá trình làm việc của vi xử lý trong một khoảng thời gian định
trước, trong ví dụ ta dùng để tạm dừng 0,5 s.