XÂY DỰNG PHẦN MỀM

Một phần của tài liệu Xây dựng mô hình cân điện tử sử dụng mạch Arduino (Trang 50 - 63)

3.1 CẤU TRÚC HỆ THỐNG

3.1.2 XÂY DỰNG PHẦN MỀM

Khai báo các hằng, biến và tạo lập các giá trị ban đầu

Như thông thường, tất cả các biến và hằng cùng với các thư viện và thủ tục đều phải được thông báo và trong mục void setup thì những thủ tục ban đầu cần được thực hiện. Phần này tương ứng với mục (1) và (2) trong sơ đồ khối chương trình. Sau đây là đoạn chương trình cho phần này.

#include <HX711.h> // Khai báo thư viện của mạch hx711 #include <EEPROM.h> //Khai báo thư viện EEPROM

#include <Servo.h> //Khai bao thu vien cua dong co servo Servo myServo; // Đặt tên động cơ servo

HX711 scale(A0, A1); // khai bao 2 chan tin hieu cua loadcell ket noi voi 2 chan analog 0 va analog 1

int ledPins[] = {6}; // khai bao cac chan ket noi led 7 doan theo thu tu abcdefg

int ledle[]={10,9,8,7}; // Khai bao cac chan ket noi voi katot cua 4 led don chi thi

38

int nutnhan =2; // Nút nhấn được kết nối với chân số 2 int bientro = A5;// Biến trở- chân A5

int chot = 13; // Chân chốt tín hiệu 7495- 13

int led7doan = 12; //Chân nhận tín hiệu nối tiếp 7495- 12 int ck =6;// Xung ck 7495 - 6

boolean macc[14][8]= //mã led 7 đoạn catot chung { {1,1,1,1,1,0,1,0},//0- ABCDEFGHDi {0,1,1,0,0,0,0,0},//1 {1,1,0,1,1,1,0,0},//2 {1,1,1,1,0,1,0,0},//3 {0,1,1,0,0,1,1,0},//4 {1,0,1,1,0,1,1,0},//5 {1,0,1,1,1,1,1,0},//6 {1,1,1,0,0,0,0,0},//7 {1,1,1,1,1,1,1,0},//8 {1,1,1,1,0,1,1,0},//9 {1,0,0,1,0,1,0,0},//10 ngang {0,1,1,0,1,0,1,0},//11 doc {0,0,0,0,0,1,0,0},//12 vài kiểu khác {0,0,0,0,0,0,0,0},//13 };

const int gocmin=167; //đặt góc khi động cơ đóng van

const int gocmax=140;// Đặt góc khi động cơ mở hồn tồn van int save = 0; //Một số biến sử dụng và giá trị khởi tạo

int zero=0; int giatridat; float cannang; int lamtron;

39 float tong=0; int tonggoc=0; int dem=0; float trungbinh; int trungbinhgoc; String inputString = ""; boolean stringComplete = false; boolean tudong =1; boolean nhangiatridat =0; int gocmoservo; int giatrinhan=0; char kytudau; int demthoigian=0;

void setup() // Chương trình chỉ chạy 1 lần khởi động {

for(int index = 3; index < 14; index++) //khai báo chân tín hiệu ra

{pinMode(index, OUTPUT);}

pinMode(2, INPUT); // khai báo chân tín hiệu vào scale.set_scale(2280.f); //thủ tục cho loadcell scale.tare();

Serial.begin(9600); //khai báo cổng nối tiếp tốc độ truyền 9600bit/s

inputString.reserve(200); //khai báo dung lượng dự trữ cho biến inputString

myServo.attach(11); //Khai báo chân điều khiển servo myServo.write(gocmin); //Đóng van

for (int i=0; i<6; i++) // Nhay led de bao reset {

40

songuyen(5); tled7doan(11, 11); delay (50); songuyen(6); tled7doan(10, 10); delay (50); }

giatridat=EEPROM.read(save)*100; //lấy lại giá trị đặt trong epprom

serialEvent(); //gọi chức năng sự kiện nối tiếp

attachInterrupt(0, ngat, LOW);//Khai báo ngắt ngoài 0 }

Chương trình chính

Chương trình chính của cân điện tử được lập trình bao gồm các phần từ (3) đến (8). Trong đoạn trương trình này sử dụng nhiều chương trình con sẽ được trình bày ở mục tiếp theo. Chương trình chính của cân điện tử được viết như sau:

void loop()

{

while(1)//Tạo vịng lặp vơ hạn {

serialEvent(); //Gọi chức năng sự kiện nối tiếp

if(demthoigian !=0) //Kiểm tra thời gian nhấn nút (3) { if (demthoigian<500){ demthoigian=0; resetzero (zero); //(5) } else { demthoigian=0; nhapgiatridat(giatridat); //(4) } }

41

cannang=scale.get_units()*5-zero; //Nhận giá trị và xử lý (6)

lamtron= round(cannang/10)*10;

if(tudong==1)gocmoservo=goc(giatridat, cannang);

myServo.write(gocmoservo); //Tính góc quay cho servo tonggoc+=gocmoservo; tong+=cannang; dem+=1; if(dem>=5) { trungbinh=tong*1.0/dem; //tính trung bình (7) trungbinhgoc=tonggoc/dem; trungbinhgoc=(gocmin-trungbinhgoc)*100/(gocmin-gocmax);

Serial.print(trungbinh,0); //Gửi dữ liệu qua cổng

serial

Serial.print(";");

Serial.print(trungbinhgoc); Serial.print(";");

Serial.println(giatridat);

hienthi(trungbinh); //Hiển thị ra cơ cấu hiển thị dem=0;

tong=0; tonggoc=0; }

if (trungbinh<-50 and dem==4) //phần sử lý hiển thị khi chỉ số âm

tled7doan(12, 12);

if(stringComplete == true){ // Xử lý tín hiệu đến (8) xulydulieuden(inputString);

42 stringComplete = false; inputString=""; } } }

Cần tạo một vịng lặp vơ hạn bao bọc tất cả các lệnh trong chương trình chính để các biến sử dụng trong chương trình khơng bị mất giá trị.

Kiểm tra thời gian nhấn nút và xử lý các trường hợp

Trong phần kiểm tra thời gian nhấn nút (3), biến demthoigian sẽ được kiểm tra để phân trường hợp sẽ thực hiện việc nhập giá trị đặt (4) hay resetzero (5). Biến demthoigian là giá trị đếm được tương ứng với thời gian nhấn nút được thực hiện thơng qua chương trình ngắt ngồi điều khiển bằng nút nhấn. Việc nhập giá trị đặt hay resetzero được viết trong các chương trình con riêng. Sau khi phân trường hợp xong, nếu biến demthoigian khác 0 sẽ được đưa về 0 ngay vì trong chương trình con nhập giá trị đặt cần phải sử dụng lại biến này. Nếu khơng, chương trình con thực hiện nhập giá trị đặt sẽ khơng hoạt động như mong muốn.

if(demthoigian !=0) //Kiểm tra thời gian nhấn nút (3) { if (demthoigian<500){ demthoigian=0; resetzero (zero); //(5) } else { demthoigian=0; nhapgiatridat(giatridat); //(4) } }

Trong thời gian nhấn nút thì cứ 50ms thì biến thời gian sẽ được tăng lên 1 đơn vị. Đoạn chương trình ngắt sau sẽ thể hiện cơng việc của chíp điều khiển khi nhấn nút:

43

{

demthoigian+=1; delay(50);

}

Chương trình con resetzero được viết như sau:

void resetzero (int &zero) // lay lai muc zero {

zero=scale.get_units()*5; // gan zero bang can nang hien tai

for (int i=0; i<7; i++){ // Nhay led de bao reset songuyen(5); tled7doan(10,10); delay (50);

songuyen(6); tled7doan(11,11); delay (50); }

}

Chương trình con nhập giá trị đặt được viết như sau: void nhapgiatridat (int &giatridat)

{

while (1) //tao vong lap mai mai de bien zero khong bi xoa { hienthi(round(analogRead(bientro)*5/100)*100); if(demthoigian!=0) { demthoigian=0; giatridat=(round(analogRead(bientro)*5/100)*100); //gan giatrihienthi cho giatridat, Lam tron toi hang tram

EEPROM.write(save, giatridat/100); // Luu giatri dat vao eeprom

for(int n=0; n<3; n++) //hiển thị báo hiệu nhận giá trị

44

{

hienthi(round(analogRead(bientro)*5/100)*100); delay (200);

songuyen(0); tled7doan(13,13); delay(100); }

break; }

} }

(xem chương trình con hienthi trong mục 3.2.7)

Nhận giá trị cân nặng, đặt góc cho động cơ servo Code : cannang=scale.get_units()*5-zero; //Nhận giá trị (6) lamtron= round(cannang/10)*10; if(tudong==1)gocmoservo=goc(giatridat, cannang); myServo.write(gocmoservo); tonggoc+=gocmoservo; tong+=cannang; dem+=1;

Công việc nhận giá trị cân nặng và đặt góc cho động cơ servo thực hiện nhờ một số câu lệnh được đánh dấu (6) trong đoạn chương trình chính. Trong đó, câu lệnh nhận giá trị cân nặng từ loadcell là cầu lệnh :

cannang=scale.get_units()*5-zero;

Trong đó, giá trị nhận được được gọi bằng hàm scale.get_units(), sau đó được nhân 5 vì loadcell thuộc loại 5kg và trừ đi biến zero là biến căn chỉnh cho việc resetzero tương ứng với giá trị cân nặng của đĩa cân. Mặc định ban đầu giá trị của biến zero bằng 0 vì mặc định, thư viện hx711 sẽ trả về giá trị cân nặng khi khởi động hệ thống (dù có vật nặng trên cân hay khơng) là 0. Biến này sẽ được thay đổi trong chương trình con resetzero.

45

Việc điều khiển động cơ servo phụ thuộc vào chế độ hoạt động của van là tự động hay bằng tay. Mặc định là chế độ tự động, chế độ này chỉ được thay đổi bằng tín hiệu gửi từ cổng nối tiếp. Có thể tham khảo phần xử lý tín hiệu đến để hiểu rõ hơn về chế độ này.

Trong khi xử lý góc quay cho động cơ servo, có sử dụng một hàm là hàm góc được viết như sau :

int goc (int giatridat, int lamtron) {

int goccanquay;

int doquay=gocmin-gocmax;

if (lamtron <= giatridat + 5) return gocmin;

else if (lamtron >= giatridat + 60) return gocmax;

else goccanquay= (lamtron-giatridat)*(lamtron-

giatridat)/150+8.5;

return gocmin-goccanquay; }

Hàm góc có kiểu trả về là kiểu interger với giá trị phụ thuộc vào 2 biến là biến giá trị cân nặng được làm tròn : lamtron và giá trị đặt : giatridat. Ngồi ra, cịn sử dụng 2 hằng số quy định góc quay tối thiểu và tối đa cho động cơ. Hàm sẽ giới hạn và tính tốn góc quay cho động cơ bắt đầu tăng goc mở khi có sai lệch bắt đầu từ lớn hơn 5g và sẽ mở góc tối đa khi khối lượng cân được lớn hơn giá trị đặt 60g. Hàm tính tốn là hàm bậc hai, được viết dựa vào thử nghiệm thực tiễn.

Tính trung bình và hiển thị giá trị cân nặng, gửi dữ liệu quan cổng serial Code : if(dem>=5) { trungbinh=tong*1.0/dem; //tính trung bình (7) trungbinhgoc=tonggoc/dem; trungbinhgoc=(gocmin-trungbinhgoc)*100/(gocmin-gocmax);

46

Serial.print(trungbinh,0); //Gửi dữ liệu qua cổng

serial

Serial.print(";");

Serial.print(trungbinhgoc); Serial.print(";");

Serial.println(giatridat);

hienthi(trungbinh); //Hiển thị ra cơ cấu hiển thị

dem=0; //xóa các biến liên quan để chuẩn bị cho đợt lấy mẫu mới

tong=0; tonggoc=0; }

Việc xử lý dữ liệu đến bao gồm việc làm tròn dữ liệu, cân chỉnh dữ liệu, lấy mẫu, và tính trung bình cho các đại lượng giá trị cân nặng và giá trị góc quay của động cơ servo. Trong đoạn dữ liệu này cũng tính tốn và điều khiển góc quay cho động cơ servo để đóng mở van. Việc lấy mẫu ở đây được thực hiện 5 lần và sau đó tính trung bình rồi hiển thị và gửi đi.

Dữ liệu được gửi đi bao gồm giá trị cân nặng đơn vị gam, góc của động cơ servo đơn vị phần trăm và giá trị đặt đơn vị gam. Các con số này được gửi dưới dạng chuỗi và gửi chung với nhau trong một lần gửi cách nhau bằng dấu chấm phẩy (;).

Trong phần này, quan trọng nhất là chương trình con hiển thị có chức năng xuất dữ liệu ra cơ cấu hiển thị. Đoạn chương trình con hiển thị được viết như sau :

void hienthi(float n)

{

if (n<0) n=n*-1;

int t=round(n/10); //lấy 3 số đầu của dãy 5 số thể hiện cân nặng

int donvi=t%10; //tách lấy số thứ 3 t=t/10;

47

t=t/10;

int songuyen1=t%10; // tách lấy số đầu tiên songuyen(songuyen1);

tled7doan(chuc,donvi); }

Chương trình con hienthi có chức năng tách lấy ra trong tham số n 3 con số đầu tiên để hiển thị ra cơ cấu hiển thị, cụ thể lấy số đầu tiên sẽ được thể hiện bằng 4 led đơn (sử dụng chương trình con songuyen), số thứ 2 và số thứ 3 được thể hiện ra 2 con led 7 đoạn (sử dụng chương trình con tled7doan). Hai đoạn chương trình con này được viết như sau :

void songuyen(int b) //led chi thi 0-4

{

if(b==5) // b=5 va 6 la hai cach hien thi de the hien khi reset muc zero

{ digitalWrite(ledle[0], HIGH); digitalWrite(ledle[1], LOW); digitalWrite(ledle[2], HIGH); digitalWrite(ledle[3], LOW);} else if (b==6) {digitalWrite(ledle[0], LOW); digitalWrite(ledle[1], HIGH); digitalWrite(ledle[2], LOW); digitalWrite(ledle[3], HIGH);} else

for (int i=0; i<5; i++) // khi b = 0 ko co led sang, b =1 led 1 sang...

{

if(i<b) digitalWrite(ledle[i], HIGH); else digitalWrite(ledle[i], LOW);

48

}

void tled7doan(int chuc, int donvi)

{

for (int i=7; i>=0; i--){ //Truyen 7 bit don vi digitalWrite(ck, LOW);

digitalWrite(led7doan, macc[donvi][i]); digitalWrite(ck, HIGH);

}

for (int i=7; i>=0; i--){ //Truyen 7 bit hang chuc digitalWrite(ck, LOW);

digitalWrite(led7doan, macc[chuc][i]); digitalWrite(ck, HIGH);

}

digitalWrite(chot, LOW); // cho phep hien thi digitalWrite(chot, HIGH);

}

Trong chương trình con songuyen ngồi việc hiển thị các con số từ 0 đến 4 tương ứng với 0

led sáng đến 4 led sáng thì 4 led còn hiển thị giá trị 5 và 6 để phục vụ cho các trường hợp thông báo, nhấp náy.

Hàm tled7doan dùng các lệnh digitalWrite để điều khiển 3 chân vào cho ic74595 : ic ghi dịch có chức năng chuyển đổi tín hiệu nối tiếp thành tính hiệu song song, rất có ích trong việc mở rộng các chân tín hiệu số đầu ra. Ở đây, tác giả sử dụng 2 con ic 74595 mắc kiểu nối tiếp nhau nên chỉ cần điều khiển 3 chân là chân tín hiệu, chân chốt và chân xung ck. Như vậy, mỗi ic 74595 điều khiển được 7 chân tương ứng với 1 led 7 đoạn.

Xử lý tín hiệu điến Code :

if(stringComplete == true) // Xử lý tín hiệu đến (8) {

49

xulydulieuden(inputString); //gọi chương trình con xulydulieuden

stringComplete = false; //xóa các biến liên quan để nhận đợt dữ liệu mới

inputString=""; }

Việc xử lý tín hiệu đến chỉ được thực hiện khi biến stringComplete có giá trị là true và cơng việc chủ yếu nằm ở chương trình con xulydulieuden, chương trình con này được viết như sau :

void xulydulieuden (String x) {

char kytudautien = x.charAt(0); //tách lấy ký tự đầu tiên int giatrinhan=0;

if (kytudautien=='r') resetzero (zero); if (kytudautien=='m') { tudong=0; gocmoservo=gocmax; } if (kytudautien=='d'){ tudong=0; gocmoservo=gocmin; } if (kytudautien=='t') tudong=1; if (kytudautien=='s'){ x.remove(0,1); giatrinhan= x.toInt(); giatridat=giatrinhan;

EEPROM.write(save, giatridat/100); // Luu giatri dat vao eeprom

50 { hienthi(round(giatridat*1.0/100)*100); delay (200); songuyen(0); tled7doan(13,13); delay(100); } } }

Trong đó, x là tham trị của chương trình con được đặt vào bằng chuỗi nhận được từ cổng nối tiếp. Sau đó cơng việc so sánh ký tự đầu tiên được thực hiện và thực hiện các công việc tương ứng. Riêng với lệnh đặt giá trị đặt mới (khi nhận được chữ s đầu tiên) thì cần tiếp tục nhận chuỗi số sau chữ s để lấy giá trị đặt. Hàm resetzero đã được trình bày trong mục 2.3.5

Phải kể đến là một đoạn code nhỏ phục vụ cho việc nhận dữ liệu nối tiếp đến, đó là chương trình con serial event :

void serialEvent() {

while (Serial.available()) {

// get the new byte:

char inChar = (char)Serial.read(); // add it to the inputString:

inputString += inChar; } stringComplete = true; }

Như vậy, mỗi khi có tín hiệu nối tiếp đến thì chíp sẽ thực hiện lưu chuỗi nhận được vào biến inputString và đặt cho biến stringComplete = true để chuẩn bị cho việc xử lý tín hiệu đến được trình bày ở phần trên.

Một phần của tài liệu Xây dựng mô hình cân điện tử sử dụng mạch Arduino (Trang 50 - 63)

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

(67 trang)