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 đếm sĩ số lớp sử dụng Board Arduino (Trang 43 - 55)

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

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

32

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 <NewPing.h> #include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

#define TRIGGER_PIN 7 // Arduino pin tied to trigger pin on the ultrasonic sensor. #define ECHO_PIN 8 // Arduino pin tied to echo pin on the ultrasonic sensor.

#define MAX_DISTANCE 240 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

#define so_mau 80 // chieu dai mau #define nguong_mau 30 // chieu dai mau #define on_dinh 12

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

unsigned char Dis[so_mau]={0,7,6,7,3,6,4,8,9,0,0,7,6,7,3,6,4,8,9,0}; unsigned int HD=0,RA=0,VAO=0;

unsigned int index=0;

unsigned char Cout_Valic();

unsigned char Cout_Last_Invalic(); unsigned char Cout_INC();

unsigned char Cout_DEC(); unsigned char First_Dis(); unsigned char Last_Dis(); void Clear_Dis();

33 void Dis_input();

void setup() {

Serial.begin(9600); // Open serial monitor at 115200 baud to see ping results. // set up the LCD's number of columns and rows:

lcd.begin(16, 2);

// Print a message to the LCD. lcd.print("H.Dien:0 "); //8 ky tu

lcd.setCursor(0,1); lcd.print("Vao:0 "); //5 lcd.setCursor(8,1); lcd.print("Ra:0"); //4

}

Chương trình chính

Chương trình chính của đếm sĩ số lớp đượ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 đếm sĩ số lớp được viết như sau:

void loop() {

delay(14); // Wait 50ms between pings (about 20 pings/sec). 29ms should be

the shortest delay between pings. //Serial.print("Ping: ");

unsigned char D;

D=sonar.ping_cm(); Dis_input(D);// thu thap data

Serial.print(D); // Send ping, get distance in cm and print result (0 = outside set distance range)

Serial.println("\n");

if(Cout_Valic()>1&&index>=so_mau-1&&Cout_Last_Invalic()>on_dinh){ unsigned char tang,giam;

tang=Cout_INC(); giam=Cout_DEC(); if(tang>giam) VAO++;

34 else if(tang<giam) RA++;

else { if(First_Dis()>Last_Dis()) RA++; else VAO++;

}

index=0; //reset data Clear_Dis();

lcd.setCursor(4+1, 1); lcd.print(" "); lcd.setCursor(4, 1); lcd.print(RA); //ra lcd.setCursor(11+1, 1); lcd.print(" "); lcd.setCursor(11, 1); lcd.print(VAO); lcd.setCursor(7+1, 0); lcd.print(" ");

if(VAO>RA) HD=VAO-RA; else HD=RA-VAO; lcd.setCursor(7, 0); lcd.print(HD); //ra

} }

unsigned char Cout_Valic(){ unsigned char i=0,cout=0; for(;i<so_mau;i++)

if(Dis[i]>nguong_mau) cout++; return cout;

}

unsigned char Cout_INC() { if(Cout_Valic()>1){

unsigned char i=0,cout=0,tam=0;

for(;(i<so_mau-1)&&(Dis[i]<=nguong_mau);i++) ;

tam=Dis[i]; //Xac dinh duoc vi tri gia tri mau hop le

35 if(Dis[i+1]>tam){ cout++; tam=Dis[i+1]; } else if(Dis[i+1]>nguong_mau) tam= Dis[i+1]; } return cout; } }

unsigned char Cout_DEC() { if(Cout_Valic()>1){

unsigned char i=0,cout=0,tam=0;

for(;(i<so_mau-1)&&(Dis[i]<=nguong_mau);i++) ;

tam=Dis[i]; //Xac dinh duoc vi tri gia tri mau hop le for(;i<so_mau-1;i++) { if(Dis[i+1]<tam&&Dis[i+1]>nguong_mau){ cout++; tam=Dis[i+1]; } else if(Dis[i+1]>nguong_mau) tam= Dis[i+1]; } return cout; } } void Clear_Dis(){

for(unsigned char i=0;i<so_mau;i++) Dis[i]=0; index=0;

36 }

void Dis_input(unsigned char data){ for(char i=so_mau-2;i>=0;i--){ Dis[i+1]=Dis[i]; } Dis[0]=data; index++; }

unsigned char Last_Dis(){ unsigned char i=0;

for(;i<so_mau-1;i++){

if(Dis[i]>nguong_mau) return Dis[i]; }

}

unsigned char First_Dis(){ unsigned char i=so_mau-1; for(;i>1;i++){

if(Dis[i]>nguong_mau) return Dis[i]; }

}

unsigned char Cout_Last_Invalic(){ unsigned char i=0,cout=0;

for(;i<so_mau-1&&Dis[i]<nguong_mau;i++) ;

return i; }

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ị.

37

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:

void ngat() {

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

38 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ị

{

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)

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;

39 dem+=1;

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

Việc điều khiển 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, 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 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 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ị, 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); Serial.print(trungbinh,0); //Gửi dữ liệu qua cổng serial

Serial.print(";");

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

40 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; }

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); //

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

int chuc=t%10; // tách lấy số thứ 2 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)

41 {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); }

}

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 ngoà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

42

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) {

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();

43 giatridat=giatrinhan;

EEPROM.write(save, giatridat/100); // Luu giatri dat vao eeprom for(int n=0; n<3; n++) { 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 đếm sĩ số lớp sử dụng Board Arduino (Trang 43 - 55)

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

(59 trang)