Chia sẻ Lập trình đồng hồ LED 7 đoạn dùng ESP8266 và DS1307 với trình Arduino IDE – cập nhật thời gian trực tuyến

nhtrung1980

Học sinh phổ thông
#1
dongho1.jpg

Ưu điểm
Cập nhật thời gian thực từ Internet
Loại bỏ các phím nhấn vật lý

Linh kiện phần cứng
  1. LED 7 đoạn loại lớn sử dụng nguồn dương chung
  2. Module wifi ESP8266 nodeMCU
  3. Module đồng hồ thời gian thực DS1307 hoặc tương đương
  4. Pin CMOS dùng cho module đồng hồ
  5. IC 74HC595 + đế cho IC
  6. Mạch giảm áp DC mini
  7. 2 bóng đèn LED làm nháy giây. Trong dự án này tôi sử dụng 2 bóng LED 5050 RGB có sẵn trong kho linh kiện.
  8. Bo mạch thí nghiệm để gắn linh kiện
  9. 5 Điện trở 1K
  10. 5 Transistor A1015
  11. 1 điện trở 330ohm


Nguồn 5VDC
Cài đặt Arduino để lập trình được với ESP8266

http://dandientu.com/lap-trinh-nhung/lap-trinh-esp8266-esp-12e-nodemcu-su-dung-arduino.html
Sơ đồ khối hiển thị

Tôi tự quy ước các chân của LED 7 đoạn, theo quy ước thì chúng ta sẽ không sử dụng chân số 3 và chân số 6. Trước khi bạn sử dụng LED 7 đoạn, bạn cần kiểm tra các phân đoạn có giống trong hình tôi mô tả hay không, nếu không bạn hãy tự vẽ lại thứ tự các phân đoạn theo LED thực tế.

Chúng ta sẽ kết nối 4 LED 7 đoạn lại với nhau, các phân đoạn sẽ được nối song song với nhau, các đoạn từ A đến G sẽ kết nối với nhau thành từng dây dẫn điện riêng cho mỗi đoạn. Các chân nguồn của LED 7 đoạn sẽ được nối vào chân C của transistor A1015. Chân B của A1015 nối với điện trở 1K hoặc 10K dùng cho chân tín hiệu từ 74HC595. Chân E của A1015 dùng cấp nguồn cho LED 7 đoạn

Như vậy chúng ta có 11 đường tín hiệu trên khối LED 7 đoạn gồm 7 đường tín hiệu phân đoạn và 4 đường điều khiển tắt mở các LED, chúng ta có 1 ma trận 7X4 dùng để hiển thị thông tin.



Sơ đồ kết nối 4 LED 7 đoạn thành ma trận 7x4. Đường màu đỏ chính là chân số 10 trên LED 7 đoạn



Sơ đồ kết nối LED 7 đoạn với 74HC595.
Theo thiết kế IC1 sẽ điều khiển 7 đường dữ liệu hiển thị số và IC2 sẽ tắt mở 1 trong 4 LED, như vậy trong 1 thời gian chỉ có 1 LED 7 đoạn được hiển thị, do chạy với tốc độ cao bạn sẽ thấy cả 4 LED hiển thị cùng lúc.
Theo sơ đồ thiết kế dưới thì dữ liệu hiển thị LED 7 đoạn xuất trước rồi mới tới dữ liệu tắt mở 1 trong 4 led 7 đoạn. Nếu trong quá trình thi công thứ tự 2 IC bị đảo lại khi lập trình cũng phải đảo lại thứ tự xuất dữ liệu ra IC 74HC595.



Sơ đồ IC đồng hồ DS1307


Sơ đồ kết nối DS1307 – ESP8266 – 74HC595


Sơ đồ kết nối với led nháy giây


Vấn đề cấp nguồn
Do các LED 7 đoạn chạy điện áp tối thiểu là 4.3V, dưới mức này, LED có thể sẽ không sáng nên ta cần sử dụng module hạ áp DC Mini để điều chỉnh điện áp cho nó để đảm bảo LED sử dụng lâu bền, đồng thời cũng làm nguồn cung cấp cho module ESP8266, nối vào chân 5V của ESP8266.
Lấy mức điện áp 3.3V trên module ESP8266 để cung cấp cho module DS1307 vì các tín hiệu xuất cho ESP8366 chỉ nên sử dụng ở mức 3.3V và cũng lấy mức điện áp này cung cấp cho LED báo giây vì LED đỏ chỉ sáng ở mức 1.8V, cần sử dụng thêm điện trở 330ohm tại chân dương của LED để hạn dòng cho LED nháy giây này.

Lập trình hiển thị LED 7 đoạn

//Pin connected to latch pin (ST_CP) of 74HC595
#define LATCH_PIN 15 //D8
//Pin connected to clock pin (SH_CP) of 74HC595
#define CLOCK_PIN 14 //pin D5
//Pin connected to Data in (DS) of 74HC5
#define DATA_PIN 13 //pin D7
#define SDA_PIN 5
#define SCL_PIN 4
//Các biến lưu dữ liệu
byte numbers[10]; //biến lưu giá trị từ 0 đến 9
byte selectNumber[4]; // biến lưu giá trị tắt mở các LED từ 0 đến 3
byte displayNumber[4];
int hours = 90; // biến kiểm tra hiển thị
int minutes = 12;
void setup() {
pinMode(LATCH_PIN, OUTPUT);
//segment: EABGFDC
// các giá trị dạng nhị phân để dể thay đổi khi cần thiết, giá trị 0 đèn sáng, giá trị 1 đèn tắt
numbers[0] = 0b00010001;
numbers[1] = 0b11011101;
numbers[2] = 0b00101001;
numbers[3] = 0b10001001;
numbers[4] = 0b11000101;
numbers[5] = 0b10000011;
numbers[6] = 0b00000011;
numbers[7] = 0b11010001;
numbers[8] = 0b00000001;
numbers[9] = 0b10000001;
// các giá trị chọn LED
selectNumber[0] = 0xFD;
selectNumber[1] = 0xFB;
selectNumber[2] = 0xF7;
selectNumber[3] = 0xEF;
}

void loop() {
displayNumber[3] = numbers[hour / 10]; // lấy phần nguyên
displayNumber[2] = numbers[hour % 10]; // lấy phần dư của số
displayNumber[1] = numbers[minute / 10];
displayNumber[0] = numbers[minute % 10];

for (int j = 0; j < 4; j++) {
//ground latchPin and hold low for as long as you are transmitting
digitalWrite(LATCH_PIN, 0);
shiftOut(DATA_PIN, CLOCK_PIN, selectNumber[j]);
shiftOut(DATA_PIN, CLOCK_PIN, displayNumber[j]);
digitalWrite(LATCH_PIN, 1);
delay(5);
}
}
// viết lại hàm shiftOut thay cho hàm của Arduino
void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
int i = 0;
int pinState;
pinMode(myClockPin, OUTPUT);
pinMode(myDataPin, OUTPUT);

digitalWrite(myDataPin, 0);
digitalWrite(myClockPin, 0);

for (i = 7; i >= 0; i--) {
digitalWrite(myClockPin, 0);
pinState = myDataOut & (1 << i) ? 1 : 0;
//Sets the pin to HIGH or LOW depending on pinState
digitalWrite(myDataPin, pinState);
//register shifts bits on upstroke of clock pin
digitalWrite(myClockPin, 1);
//zero the data pin after shift to prevent bleed through
digitalWrite(myDataPin, 0);
}
//stop shifting
digitalWrite(myClockPin, 0);
}

Lập trình hiển thị đồng hồ và cập nhật thời gian từ Internet
Trong mã nguồn, sử dụng thư viện Time để xử lý thông tin thời gian. Xem trong mã nguồn đính kèm tài liệu này.
Múi giờ của Việt Nam là 7
Địa chỉ của chip đồng hồ là 0x68

#include <Wire.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <TimeLib.h>

//Pin connected to latch pin (ST_CP) of 74HC595
#define LATCH_PIN 15 //D8
//Pin connected to clock pin (SH_CP) of 74HC595
#define CLOCK_PIN 14 //pin D5
////Pin connected to Data in (DS) of 74HC5
#define DATA_PIN 13 //pin D7
#define SDA_PIN 5
#define SCL_PIN 4
#define DS1307 0x68
#define WLAN_SSID "ten_mang"
#define WLAN_PASS "password_mang_wifi"
// khai báo các biến và mảng lưu giá trị thời gian
int my_second, my_minute, my_hour, my_day, my_wday, my_month, my_year;
byte numbers[10];
byte selectNumber[4];
byte displayNumber[4];
unsigned int localPort = 2390; // local port to listen for UDP packets
// A UDP instance to let us send and receive packets over UDP
static const char ntpServerName[] = "time.nist.gov";
const int timeZone = 7;
/* Số byte dữ liệu sẽ đọc từ DS1307 */
const byte NumberOfFields = 7;
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
WiFiUDP Udp;

time_t getNtpTime();
//khai báo các hàm, trong các phiên bản mới hơn của Arduino có thể bỏ qua bước này
void digitalClockDisplay();
void printDigits(int digits);
void sendNTPpacket(IPAddress &address);

void setup() {
Wire.begin(SDA_PIN, SCL_PIN);
Serial.begin(115200);

pinMode(LATCH_PIN, OUTPUT);

//segment: EABGFDC
//thứ tự các phân đoạn có thể không giống với cách khai báo của tôi, các bạn tự thay đổi theo thực tế đi dây dữ liệu của bạn, bít đầu tiên từ bên phải là chân Q0, nó luôn luôn là 1, chúng ta không sử dụng chân Q0.
numbers[0] = 0b00010001;
numbers[1] = 0b11011101;
numbers[2] = 0b00101001;
numbers[3] = 0b10001001;
numbers[4] = 0b11000101;
numbers[5] = 0b10000011;
numbers[6] = 0b00000011;
numbers[7] = 0b11010001;
numbers[8] = 0b00000001;
numbers[9] = 0b10000001;

selectNumber[0] = 0xFD;
selectNumber[1] = 0xFB;
selectNumber[2] = 0xF7;
selectNumber[3] = 0xEF;

Serial.println("Connecting to Wi-Fi");

WiFi.mode(WIFI_STA);
WiFi.begin (WLAN_SSID, WLAN_PASS);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println("WiFi connected");

Udp.begin(localPort);

setSyncProvider(getNtpTime);
setSyncInterval(300);
setTime(hour(), minute(), second(), weekday(), day(), month() , year() );
// khởi động chân đếm giây trên module DS1307
set1hz();
}

void loop() {
readDS1307();
// xuất giá trị ra Terminal console để kiểm tra
digitalClockDisplay();
// tính toán giá trị trước khi xuất ra LED 7 đoạn
displayNumber[0] = numbers[my_hour / 10];
displayNumber[1] = numbers[my_hour % 10];
displayNumber[2] = numbers[my_minute / 10];
displayNumber[3] = numbers[my_minute % 10];
// mở từng LED và hiển thị từng số lên LED
for (int j = 0; j < 4; j++) {
digitalWrite(LATCH_PIN, 0);
shiftOut(DATA_PIN, CLOCK_PIN, displayNumber[j]);
shiftOut(DATA_PIN, CLOCK_PIN, selectNumber[j]);
digitalWrite(LATCH_PIN, 1);
delay(5);
}
}

/*-------- NTP code ----------*/
time_t getNtpTime()
{
IPAddress ntpServerIP; // NTP server's ip address

while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.println("Transmit NTP Request");
// get a random server from the pool
WiFi.hostByName(ntpServerName, ntpServerIP);
Serial.print(ntpServerName);
Serial.print(": ");
Serial.println(ntpServerIP);
sendNTPpacket(ntpServerIP);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Receive NTP Response");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response :-(");
return 0; // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}

void set1hz() {
Wire.begin();
Wire.beginTransmission(DS1307);
Wire.write(0x07);
Wire.write(0x10); // Set Square Wave to 1 Hz
Wire.endTransmission();
}
void readDS1307()
{
Wire.beginTransmission(DS1307);
Wire.write((byte)0x00);
Wire.endTransmission();
Wire.requestFrom(DS1307, NumberOfFields);

my_second = bcd2dec(Wire.read() & 0x7f);
my_minute = bcd2dec(Wire.read() );
my_hour = bcd2dec(Wire.read() & 0x3f); // chế độ 24h.
my_wday = bcd2dec(Wire.read() );
my_day = bcd2dec(Wire.read() );
my_month = bcd2dec(Wire.read() );
my_year = bcd2dec(Wire.read() );
my_year += 2000;
}
/* Chuyển từ format BCD (Binary-Coded Decimal) sang Decimal */
int bcd2dec(byte num)
{
return ((num / 16 * 10) + (num % 16));
}
/* Chuyển từ Decimal sang BCD */
int dec2bcd(byte num)
{
return ((num / 10 * 16) + (num % 10));
}

/* cài đặt thời gian cho DS1307 */
void setTime(byte hr, byte min, byte sec, byte wd, byte d, byte mth, byte yr)
{
Wire.beginTransmission(DS1307);
Wire.write(byte(0x00)); // đặt lại pointer
Wire.write(dec2bcd(sec));
Wire.write(dec2bcd(min));
Wire.write(dec2bcd(hr));
Wire.write(dec2bcd(wd)); // day of week: Sunday = 1, Saturday = 7
Wire.write(dec2bcd(d));
Wire.write(dec2bcd(mth));
Wire.write(dec2bcd(yr));
Wire.endTransmission();
}

void digitalClockDisplay() {
// digital clock display of the time
Serial.print(my_hour);
printDigits(my_minute);
printDigits(my_second);
Serial.print(" ");
Serial.print(my_day);
Serial.print(" ");
Serial.print(my_month);
Serial.print(" ");
Serial.print(my_year);
Serial.println();
}
void printDigits(int digits) {
// các thành phần thời gian được ngăn chách bằng dấu :
Serial.print(":");
if (digits < 10)
Serial.print('0');
Serial.print(digits);
}

// viết lại hàm shiftOut thay cho hàm của Arduino
void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
int i = 0;
int pinState;
pinMode(myClockPin, OUTPUT);
pinMode(myDataPin, OUTPUT);

digitalWrite(myDataPin, 0);
digitalWrite(myClockPin, 0);

for (i = 7; i >= 0; i--) {
digitalWrite(myClockPin, 0);
pinState = myDataOut & (1 << i) ? 1 : 0;
//Sets the pin to HIGH or LOW depending on pinState
digitalWrite(myDataPin, pinState);
//register shifts bits on upstroke of clock pin
digitalWrite(myClockPin, 1);
//zero the data pin after shift to prevent bleed through
digitalWrite(myDataPin, 0);
}

//stop shifting
digitalWrite(myClockPin, 0);
}

Tải toàn bộ mã nguồn và thư viện
http://www.mediafire.com/file/15ntchurd7t7vrr/74hc595_ds1307_esp8266.zip

Các tình huống thường gặp
Trong trường hợp SCL và SDA không thể kết nối được bạn hãy đảo chân 2 chân này.
Quá trình cập nhật thời gian đôi khi không thể cập nhật được và có hiện tượng hiển thị sai hoặc hiển thị 4 số 0. Bạn hãy reset chip ESP8266 vài lần.

Thứ tự 2 IC 74HC595 dùng trong việc truyền dữ liệu và điều khiển tắt mở các đèn LED 7 đoạn khác nhau thì thứ tự truyền trong phần mềm cũng cần điều chỉnh lại thì mới hiển thị được

nguồn: dân điện tử - http://dandientu.com
 
Sửa lần cuối:

Quảng cáo Google