Nguyên lý điều khiển và mạch mô phỏng trên Proteus

Một phần của tài liệu Hướng dẫn lập trình AVR rất hay (Trang 47 - 58)

4 Các bài tập ví dụ

4.5.2 Nguyên lý điều khiển và mạch mô phỏng trên Proteus

Từ các bước phân tích trên, ta đưa ra nguyên lý điều khiển như sau.

Hình 63: Nguyên lý điều khiển

Điện áp thay đổi từ 0 tới +5V từ mạch phân áp sẽ được đưa vào vi điều khiển (qua các chân ADC). Khối chuyển đổi ADC sẽ chuyển tín hiệu điện áp thành giá trị số tương ứng để vi điều khiển điều khiển các led 7 thanh hiển thị giá trị điện áp đo được.

Từ nguyên lý điều khiển và phân tích mạch điện, ta tiến hành xây dựng mạch nguyên lý mô phỏng trên Proteus với các linh kiện.

 Vi xử lý Atmega16.  Biến trở 10k.

Hình 64: Mạch môp hỏng trên Proteus

Do điện áp đặt vào kênh ADC có giá trị từ 0 tới +5V nên ta sẽ chọn điện áp tham chiếu AREF bằng điện áp đặt vào chân VACC, trên hình 64, chân AREF được nối với chân AVCC. Điện áp ra từ mạch phân áp được đưa vào chân PA0 (ADC0). Bộ chuyển đổi ADC sẽ đo điện áp tại kênh ADC0 này và vi điều khiển sẽ hiển thị giá trị đo được trên 2 led 7 thanh. 2 led 7 thanh sẽ được điều khiển bàng 2 cổng là PORT C và PORT D. Led 7 thanh (I) hiển thị chữ số hàng đơn vị, led 7 thanh (II) hiển thị chữ số sau dấu phẩy.

4.5.3 Lập trình cho vi xử lý

a) Xác định yêu cầu làm việc của vi xử lý

Theo như các bước phần tích yêu cầu làm việc của mạch, ta thấy vi xử lý trong ví dụ Chuyển đổi ADC cần phải sử dụng các khối là: khối chuyển đổi 1 kênh ADC, chức năng In/Out của 2 PORT C và PORT D.

Do điện áp đặt vào kênh ADC có giá trị từ 0 tới +5V nên ta sẽ chọn điện áp tham chiếu AREF bằng điện áp đặt vào chân VACC, tức bằng +5V.

Để giảm nhẹ yêu cầu tính toán, đòng thời cũng để phù hợp với khả năng quan sát của mắt người, ta yêu cầu vi xử lý tiến hành đo điện áp và hiển thị lên led 7 thanh theo chu kỳ 0,25s.

b) Xây dựng thuật toán điều khiển

Thuật toán điều khiển của ví dụ Chuyển đổi ADC có thể lấy như trong hình sau.

Hình 65: Thuật toán điều khiển

Điện áp đặt tại chân PA0 sẽ được bộ chuyển đổi ADC đo và trả về giá trị số tương ứng (từ 0 tới 1023 ứng với giá trị điện áp từ 0 đên +5V). Từ giá trị số này sẽ phải qua bước quy đổi, để tính ra điện áp, cũng như chữ số hàng đơn vị và chữ số hàng chục của giá trị điện áp. Hai chữ số này sẽ được hiển thị trên led 7 thanh qua các công PORT C và PORT D. Các bước trên sẽ lặp lại với chu kỳ 0,5s.

Như vậy người đọc có thể thấy rằng các bước trên sẽ được thực hiện trong vòng lặp

while chính. Trong bài viết này sẽ giới thiệu khái niệm về chương trình con. Đây là cách

thức hay được dùng trong lập trình, nhất là lập trình các dự án phức tạp.

Tính giá trị hàng đơn vị và 1 chữ số sau dấu phẩy

Như đã giới thiệu, sau khi đo điện áp, bộ chuyển đổi sẽ trả về giá trị số nguyên (đặt là

ADC_val) từ 0 tới 1023 ứng với khoảng điện áp đo được. Công thức điện áp đặt vào kênh

ADC là:

5.

1023

in

VADC_val

Giá trị Vin thu được có thể là số lẻ, chẳng hạn 2,28…V. Nếu vậy thì quá trình xác định chữ số sau dấu phẩy (chữ số “2”) sẽ phải dùng các thuật toán phức tạp. Để đơn giản ta tìm cách đưa 1 chữ số sau dấu phẩy sang bên trái dấu phẩy. Để làm điều đó chúng ta nhân giá trị thực của điện áp cần đo Vin với 10, được giá trị điện áp tính toán Vtt.

tt

ADC_val

V = 5. .10

1023

Nếu ban đầu chúng ta đặt kiểu biến cho biến Vtt là giá trị nguyên dương (char, int, …), thì với giá trị ví dụ của Vin=2,28…V, ta sẽ thu được giá trị của Vtt là: 22,8… làm tròn

thành 23, tức là Vtt=23. Để xác định được chữ số “2”, ta lại tiến hành chia Vtt cho 10 và gán cho một biến nguyên dương, đặt là dvi (đơn vị).

dvi = Vtt/10 = 23/10 = 2,3 làm tròn thành dvi = 2

Để xác định được chữ số “3”, ta vẫn chia Vtt cho 10, nhưng lấy phần dư và gán cho biến nguyên sdp (sau dấu phẩy).

sdp = Vtt-10*dvi = 23-10*2 = 3 Hiển thị ra led 7 thanh

Như đã trình bày, việc hiển thị các chữ số lên led 7 thanh được quyết định bởi tổ hợp trạng thái các chân trong PORT điều khiển. Ở các ví dụ trước, người đọc đã biết cách điều khiển trạng thái của một chân trong PORT bằng lệnh (PORTx.y = z;). Tuy nhiên nếu phải điều khiển cùng lúc cả 8 chân trong một cổng (trong trường hợp này là để điều khiển led 7 thanh), người ta sử dụng lệnh

PORTx = z;

Trong đó: x là tên Port cần gán trạng thái (cả 8 chân).

z là giá trị gán.

Giá trị trạng thái của mỗi chân hoặc bằng 0 hoặc bằng 1, để dùng lệnh trên, người ta xếp trạng thái cần gán của 8 chân trong Port lần lượt cạnh nhau, làm thành một số nhị phân.

Chân số 7 6 5 4 3 2 1 0 Trạng thái 1 1 1 1 1 0 0 1 Số z 1 1 1 1 1 0 0 1

Bảng trạng thái các chân của PORTC để hiển thị số 1 trên led 7 thanh.

Như trên mạch mô phỏng, để PORT C điều khiển led 7 thanh hiển thị số 1, thì hai đoạn led b và c sáng, ứng với hai chân PC1 và PC2 ở trạng thái 0, còn các chân khác ở trạng thái 1. Từ bảng trên suy ra số z bằng 11111001 trong hệ nhị phân. Nếu chuyển sang hệ cơ số 16 thì z bằng F9. Như vậy nếu muốn gán trạng thái PORTC để hiển thị số 1 như trên, thì ta viết:

PORTD = 0b11111001; hoặc

PORTD = 0xF9;

Với sơ đồ nối dây như mạch mô phỏng, ta có mã hex (cơ số 16) của các chữ số trên led 7 thanh như sau.

c) Chuẩn bị lập trình bằng phần mềm CodeVision

Thiết lập ban đầu với các thông số: vi xử lý Atmega16, thạch anh 4Mhz, tất cả các chân của PORT C và PORT D là “out”, kích hoạt ADC.

Các bước thiết lập chức năng In/Out cho các PORT C và PORT D làm tương tự như các ví dụ trước, việc kích hoạt chức năng ADC theo như hình sau.

Hình 67: Kích hoạt chức năng ADC

Trong hộp thoại CodeWizardAVR chọn ADC, đánh đấu vào mục “ADC Enabled”. Trong ô “Volt. Ref:” chọn AVCC pin để chọn điện áp tham chiếu cho khối ADC là điện áp +5V tại chân AVCC.

Trong hộp thoại “Clock:”, click chọn tần số làm việc của bộ ADC. Tần số làm việc càng cao, tốc độ chuyển đổi càng nhanh, nhưng sai số càng sao. Trong ví dụ chọn này chọn giá trị tần số làm việc cảu bộ ADC là 250 kHz.

d) Viết chương trình điều khiển

Trong ví dụ này có dùng hàm delay, nên trước tiên hãy include file delay.h (nếu chương trình chưa include file delay.h sẵn).

Khai báo các biến sau.

Vtt kiểu char chứa giá trị điện áp tính toán.

dvi và sdp kiểu char chứa giá trị chữ số hàng đơn vị và chữ số sau dấu phẩy.

Chương trình ADC và tính các chữ số

Trong chương trình, sau khi đã kích hoạt bộ ADC ở phần chuẩn bị, việc yêu cầu vi xử lý tiến hành đọc giá trị ADC được thực hiện bằng dòng lệnh.

read_adc(0);

Trong đó read_adc(0) là chương trình được phần mềm tạo sẵn cho chức năng ADC, trong dấu ngoặc () là số thứ tự kênh ADC cần đo. Trong ví dụ ta đưa điện áp vào kênh

ADC0, nên số thứ tự kênh ADC cần đo là 0. Khi qua lệnh này, vi xử lý sẽ đo điện áp tại kênh ADC0 và trả về giá trị số tương ứng.

Chương trình tính giá trị điện áp và các chữ số cần tìm như sau.

Vtt=5*10*read_adc(0)/1023; dvi=Vtt/10;

Toán tử VÀ (&)

Theo như yêu cầu hoạt động của mạch, 2 led 7 thanh dùng để hiển thị điện áp đo được với 1 chữ số sau dấu phẩy, như vậy led 7 thanh (I) phải luôn sáng dấu chấm (dp) để hiển thị dấu phẩy. Có hai cách để làm cho dấu chấm trên led 7 thanh (I) luôn sáng là.

 Viết mã hex lại cho led 7 thanh (I) với trạng thái chân PC7 luôn bằng 0 và điều khiển trạng thái PORT C bằng mã hex này.

 Sử dụng phép toán logic để đưa mã hex ở hình 65 thành mã hex mới với trạng thái chân PC7 luôn bằng 0.

Quá trình viết lại mã hex để hiển thị dấu phẩy (dp) tương tự như viết mã hex đã trình bày ở trên, người đọc hãy tự làm để có thể tự mình viết được các mã hex cho led 7 thanh. Trong bài ví dụ này sẽ trình bày các sử dụng phép toán logic, để người đọc được tiếp cận với cách viết code khác so với các ví dụ từ đầu tài liệu.

Như đã trình bày, để hiện thị chữ số sau dấu phẩy ở led (II), ta dùng lệnh.

PORTD = z;

Trong đó z là mã hex trong bảng 65 ứng với số cần hiển thị (giá trị của biến sdp), ví dụ như để hiển thị số 2, ta viết.

PORTD = 0xA4;

Để điều khiển led 7 thanh (I) hiển thị chữ số với dấu phẩy (dp) luôn sáng (ví dụ như để hiển thị chữ số “2.”, ta dùng toán tử logic VÀ “&” như sau.

PORTC = 0xA4 & 0x7F;

Trong đó A4 là mã hex của chữ số 2 không dấu chấm (trong bảng 65), còn 7F trong hệ cơ số 16 chính là 01111111 trong hệ nhị phân. Để giải thích rõ hơn, hãy xem bản dưới.

Thứ tự chân 7 6 5 4 3 2 1 0

Z = 0xA4 1 0 1 0 0 1 0 0

0x7F 0 1 1 1 1 1 1 1

0xA4 & 0x7F 0 0 1 0 0 1 0 0

PORTC 0 0 1 0 0 1 0 0

Bảng trạng PORTC sau phép toán VÀ (&).

Nếu coi trạng thái 1 ứng với “đúng” (Đ), còn trạng thái 0 ứng với “sai” (S), theo suy luận logic thông thường ta sẽ có.

 Chân 7: Đ và S sẽ thành S.

 Chân 6, 4, 3, 1, 0: S và Đ sẽ thành S.  Chân 5, 2: Đ và Đ sẽ thành Đ.

So sánh trạng thái PORTD hiển thị chữ số “2” và trạng thái PORTC hiển thị chữ số “2.” theo bảng sau.

PORTD (“2”) 1 0 1 0 0 1 0 0

PORTC (“2.”) 0 0 1 0 0 1 0 0

So sánh trạng thái PORT D và PORTC khi cùng hiển thị số “2”.

Ta thấy sau khi dùng phép toán VÀ (&), từ trạng thái như của PORTD (0xA4) sẽ chuyển thành trạng thái như của PORTC (0xA4 & 0x7F), chỉ khác nhau ở bit số 7 (điều khiển đoạn led dp). Bit số 7 của PORTC luôn có giá trị 0, vì vậy đoạn led dp của led 7 thanh (I) luôn sáng.

Chương trình hiển thị led

Về cơ bản, sau khi biết về phép so sánh if ở các ví dụ trên, người đọc sẽ nhận ra rằng có thể dùng nhiều phép so sánh if để điều khiển trạng thái các PORT điều khiển led, ví dụ để điều khiển led (II).

 Nếu sdp = 0, gán trạng thái PORTD = 0xC0;  Nếu sdp = 1, gán trạng thái PORTD = 0xF9;

 …

Như vậy để điều khiển một led 7 thanh hiển thị 10 chữ số từ 0 tới 9, cần phải có 10 phép so sánh if, 2 led 7 thanh thì sẽ phải dùng 20 phép so sánh if. Để giảm bớt khối lượng dòng lệnh (nhằm tiết kiệm bộ nhớ và giảm tải cho vi xử lý), người ta gộp nhiều lệnh if thành lệnh

swich…case.

Lấy ví dụ điều khiển hiển thị led 7 thanh (II) để hiển thị 10 số từ 0 đến 9, cấu trúc lệnh

swich…casenhư sau.

Hình 68: Lệnh switch...case điều khiển PORTD

Ý nghĩa của cấu trúc swich…case trên như sau. Vi xử lý sẽ so sánh giá trị của biến

sdp, nếu sdp = 0 thì thực hiện các lệnh 0, nếu sdp = 1 thì sẽ thực hiện các lệnh 1,… Các lệnh 0, 1, 2,… sẽ gán trạng thái cho PORTD bằng giá trị mã hex tương ứng.

Cấu trúc điều khiển PORTC cũng tương tự, chỉ khác ở chỗ gia trị gán cho PORT được thay đổi để hiển thị dấu phẩy (dp).

Hình 69: Cấu trúc switch...case điều khiển PORTC

Chương trình con

Hai đoạn chương trình điều khiển hiển thị led trên có thể đặt trực tiếp vào vòng lặp while, khi đó vòng lặp while chính sẽ có dạng.

Hình 70: Cấu trúc vòng lặp while

Người đọc hãy viết thêm vào trong vòng lặp while ở trên và thử chạy mô phỏng trên Proteus. Tuy nhiên để quá trình lập trình rõ ràng, dễ theo dõi, với các tập lệnh có thể được nhóm lại như các lệnh swich…case ở trên, người ta thường dùng chương trình con.

Hình 71: Hình dung về chương trình con

Như hình 70, nhóm các lệnh 1,2,…,n thay vì được viết trong vòng lặp while chính, có thể được nhóm lại trong chương trình con x. Khi muốn thức hiện các lệnh 1,2,…,n này, ta chỉ việc đưa tên “chương trình con x” vào vòng lặp while chính.

Như vậy, việc tách một hoặc nhiều nhóm lệnh ra thành chương trình con sẽ giúp người lập trình dễ nẵm bắt được quá trình lập trình, đồng thời giảm số lượng dòng code trong vòng lặp while chính trong trường hợp cần phải dùng nhiều lần các tập lệnh giống nhau.

Trong chương trình CodeVision, các chương trình con được viết bên ngoài trước chương trình chính void main(void), trong trường hợp bài ví dụ này, hai chương trình con điều khiển led (I) và (II) được viết sau khi khai báo biến như trong hình.

Hình 72: Hai chương trình con

Trên hình 71 là hai chương trình con. Chương trình con led1 điều khiển hiển thị trên

led 7 thanh (I), chương trình con led2 điều khiển hiển thị trên led (II). Sau đây sẽ trình bày cách viết chương trình con led2.

Một chương trình con được khai báo trong CodeVisionAVR theo cú pháp sau.

void “tên chương trình con” (khai báo biến) {

Tập lệnh trong chương trình con; }

Trong đó “tên chương trình con” là tên tùy ý theo người lập trình, với điều khiển không có dấu cách, không có chữ số đứng đầu, không dùng ký hiệu đặc biệt. Trong lập trình không đuwọc đặt hai chương trình con có tên giống nhau. Trong ví dụ trên, ta đặt tên chương trình con điều khiển hiển thị led 7 thanh (II) là led2.

Phần khai báo biến trong dấu ngoặc ()là nơi khai báo các biến chỉ dùng trong chương

chương trình, gọi là biến toàn cục, ví dụ như biến dvi và sdp). Trong chương trình con led2 ta thấy không có biến cục bộ (chỉ có câu lệnh switch...case với biến dvi hoặc sdp), trong trường hợp này trong dấu ngoặc sẽ được viết là (void).

Như vậy bố cục của chương trình con led2 sẽ như sau.

void led1(void) {

switch...case điều khiển led II; }

Sau khi đưa toàn bộ cấu trúc switch...case điều khiển led (II), ta có toàn bộ chương trình con led2 như sau.

Hình 73: Chương trình con led2

Tương tự, chương trình con led1 được viết như sau.

Hình 74: Chương trình con led1

Nếu hiểu sơ bộ về chương trình con, người đọc cũng sẽ thấy rằng hàm read_adc(0) trình bày ở trên cũng là một chương trình con được phần mềm tạo sẵn để sử dụng.

Vòng lặp while chính

Sau khi khai báo biến và viết chương trình con như các bước trên, vòng lặp while chính điều khiển đo và hiển thị điện áp như sau.

Hình 75: Vòng lặp while chính

Người đọc hãy tự viết các phần đã trình bày ở trên vào phần mềm CodeVisionAVR, kiểm tra và chạy thử trên Proteus.

Hình 76: Chạy mô phỏng trên Proteus

Nếu làm đúng như các bước trên, điện áp đầu ra mạch phân áp sẽ được đo và hiện thị trên led 7 thanh như hình trên. Dùng chuột thay đổi giá trị biến trở để thay đổi hiển thị trên led 7 thanh.

Tóm tắt

Mạch phân áp, led 7 thanh Anốt chung.

Sử dụng chức năng ADC, tính điện áp cần đo và các chữ số cần thiết.

Điều khiển trạng thái của các PORT.

Chương trình con.

Mở rộng

Hiển thị 2 led 7 thanh ở bài ví dụ bằng phương pháp quét led (chung một PORT điều khiển, mỗi led 7 thanh sáng rồi tắt luân phiên trong 5ms).

Một phần của tài liệu Hướng dẫn lập trình AVR rất hay (Trang 47 - 58)

Tải bản đầy đủ (PDF)

(66 trang)