راه‌اندازی ماژول DS3231 با آردوینو

0
959
راه‌اندازی ماژول DS3231 با آردوینو
راه‌اندازی ماژول DS3231 با آردوینو

مقدمه

شاید تاکنون در انجام پروژه‌هایتان به چالشی با عنوان سنجش زمان، تاریخ و دما برخورد کرده باشید، به‌طورمثال زمان‌سنجی مقدار عملکرد دستگاه، انجام یک عملکرد خاص طبق زمان‌بدی تعیین‌شده، چک‌کردن دما و گزارش هفتگی، ماهانه و سالیانه عملکرد دستگاه و…

اما مطمئنا اگر در پروژه‌هایتان با مواردبالا برخوردکرده‌باشید. حتما با سوالاتی همچون موارد‌زیر برخورد کرده‌اید.

  • از چه آی‌سی باید استفاده‌کنیم؟ که قابلیت دریافت همزمان تاریخ، زمان و دما را داشته‌باشد؟
  • چطور باید آن‌را راه‌اندازی کنیم؟

در این مقاله می‌خواهیم به این سوالات پاسخ‌دهیم و درنهایت ماژول معرفی‌شده را توسط برد آردوینو راه‌اندازی کنیم.

از چه آی‌سی باید استفاده‌کنیم؟ که قابلیت دریافت همزمان تاریخ، زمان و دما را داشته‌باشد؟

درواقع یکی‌از بزرگترین ضعف‌های مهندسان الکترونیک، انتخاب قطعه مناسب برای انجام کار موردنیاز است، که در این بخش می‌خواهیم باتوجه‌به پروژه‌ای که تعریف کرده‌ایم، قطعه مورد‌نظر خود را انتخاب‌کنیم.

خب اگر دقت‌کنید در مقدمه مقاله گفتیم که نیاز به سنجش زمان، تاریخ و همینطور دما را داریم. برای اینکه بتوانیم قطعه موردنظر را پیدا کنیم نیاز به یک جستجو با عنوان ماژول یا آی‌سی ساعت در گوگل را داریم که پس‌از این جستجو، با آی‌سی به نام DS3231 آشنا‌خواهیم‌شد. البته دقت‌کنید که آی‌سی DS1307 هم موجود می‌باشد که این آی‌سی دقت کمتری نسبت‌به DS3231 را دارد.

حال مرحله بعد نیاز به بررسی دیتاشیت این ماژول داریم. که ببینیم قابلیت‌هایی که مدنظر ما است را دارد یا خیر؟

خب پس‌از بررسی دیتاشیت ماژول DS3231، مواردی که مدنظرما در این پروژه بود را ازجمله مواردزیر را دارا می‌باشد.

  • تنظیم سال
  • تنظیم ماه
  • تنظیم روز
  • تنظیم روزهای هفته
  • تنظیم ساعت
  • تنظیم دقیقه
  • تنظیم ثانیه
  • داشتن یک سنسور داخلی که قابلیت اندازه‌گیری دما از -۴۰ تا ۸۵ درجه سانتی‌گراد را دارد

خب پس‌از اینکه موفق به انتخاب آی‌سی موردنظر شدیم. باید آن‌را راه‌اندازی کنیم.

نحوه راه‌اندازی ماژول DS3231

خب در این بخش قبل‌از راه‌اندازی نیاز به یک آشنایی جزئی با این ماژول را داریم. که این آشنایی جز با مراجعه به دیتاشیت بدست نخواهد آمد.

نحوه صحیح دیتاشیت خوانی چیست؟

یکی‌دیگر از مواردی که مهندسان الکترونیک در آن ضعف دارند، خواندن صحیح دیتاشیت قطعه موردنظر است که در این بخش ابتدا می‌خواهیم به‌صورت کاملا مختصر نحوه صحیح خواندن دیتاشیت را توضیح‌دهیم و نکته‌ای که وجود دارد خیلی‌از دوستان فکر می‌کنند که حتما باید تسلط کامل به زبان‌انگلیسی داشته‌باشند تا بتوانند تسلط کافی را به دیتاشیت داشته‌باشند و درنهایت بتوانند قطعه موردنظر را راه‌اندازی کنند. خیر، برای راه‌اندازی قطعه موردنظر باید نکات کلیدی که نیاز است را از دیتاشیت جمع‌آوری کرد و درنهایت از آن نکات استفاده‌کرد البته از این نکته هم غافل نباشیم که تسلط به زبان انگلیسی بسیار می‌تواند در این روند کمک‌کننده محسوب شود.

خب فرض‌کنید ما تابه‌حال حتی اسم این قطعه را هم نشنیده‌ایم و فقط تنها برنامه‌نویسی را می‌دانیم، حال باید برای راه‌اندازی، چه کاری را ‌انجام‌دهیم.

اولین کاری که باید انجام‌دهیم، این است که بدانیم آی‌سی موردنظر از چه پروتکلی پشتیبانی می‌کند. خب اگر به عکسی که در ادامه قرار دارد دقت‌کنید این آی‌سی از پروتکل I2C جهت برقراری با ماژول استفاده می‌کند.

شماتیک DS3231
شماتیک DS3231

 

دومین مرحله یادگیری پروتکل I2C است که دقیقا چه عملیاتی را باید جهت برقراری ارتباط با میکروکنترلر انجام داد.

پروتکل I2C چیست؟

اگر به‌صورت کاملا مختصر توضیح‌دهیم، یک راه ارتباطی جهت انتقال دیتا بین میکروکنترلر و آی‌سی موردنظر است که نحوه عملکرد آن به‌صورت شکل‌زیر می‌باشد.

آدرس‌دهی I2C
آدرس‌دهی I2C

 

پروتکل I2C را با پین‌های زیر می‌شناسند.

  • (SDA (Serial Data: برای ارسال و دریافت داده بین master و slave
  • (SCL (Serial Clock: خطی که حامل سیگنال کلاک می باشد.

نحوه عملکرد پروتکل I2C

در I2C داده‌ها به‌صورت پیام‌هایی فرستاده می‌شود که خود این پیام‌ها خود به چند بخش تقسیم میشوند. هر پیام شامل یک بخش آدرس است، که همان آدرس باینری مربوط به slave موردنظر است. یک یا دو بخش مربوط به دیتا هم در پیام‌های ارسالی نیز وجود دارد. پیام همچنین شامل شرایط اولیه و پایانی(دستور شروع و پایان پروتکل)، بیت‌های read/write و بیت‌های ACK / NACK بین هربخش از پیام است.

حال پس‌از اینکه یک دیدکلی نسبت‌به پروتکل پیداکردیم نیاز است که دستورات ماژول DS3231 را مطالعه‌کنیم و ببینیم که هر دستور دقیقا چه کاربردی دارد و دقیقا کجا مورداستفاده قرار می‌گیرد.

رجیسترهای DS3231
رجیسترهای DS3231

 

خب پس‌از اینکه این دستورات را موردمطالعه قراردادیم نوبت‌به انتقال همین دستورات و انتقال دیتا توسط پروتکل I2C است که برای این بخش توصیه می‌کنیم به کتابخانه‌ای که در جدول پایین توضیح‌دادیم مراجعه‌کنید.

درواقع روندی که توضیح‌داده‌شد یک حالت کاملا کلی از خواندن دیتاشیت بود که در ادامه نحوه کدنویسی ماژول DS3231 را موردمطالعه قرار می‌دهیم.

کدنویسی ماژول DS3231

در این بخش می‌خواهیم علاوه‌بر کد آردوینو ماژول، کتابخانه‌ای هم که از آن استفاده می‌کنیم موردمطالعه قراردهیم و از عملکرد آن مطلع شویم که اگر خودمان نیاز به نوشتن یه کتابخانه به‌صورت اصولی داشتیم باید چه‌کاری را انجام‌دهیم و صرفا از کتابخانه‌های آماده استفاده‌نکنیم (چون امکانش هست یه جایی گیر کنیم و اگه واقعا همش از توابع آماده استفاده‌کنیم آخرش به مشکل بخوریم و… پس بهتره که هر چیزی رو اصولی یادبگیریم و بعدش حداقل از توابع آمادش استفاده‌کنیم)

کتابخانه نوشته‌شده ماژول DS3231

#include <Wire.h>
#include <avr/pgmspace.h>
#include "Sodaq_DS3231.h"
#include "Arduino.h"

#define EPOCH_TIME_OFF 946684800 // This is 2000-jan-01 00:00:00 in epoch time
#define SECONDS_PER_DAY 86400L
این خط ارسال دستور اماده باش به ماژول است که با دستور 0x68 شناخته می‌شود
#define DS3231_ADDRESS 0x68 //I2C Slave address

تعریف یک سری دستورات ماکرو که دقیقا از دیتاشیت آیسی انتخاب شده و کاملا مطابق 
با دستورات دیتاشیت می‌باشد. که کاملا هم خوانا نوشته شده است.
نکته اول: در نوشتن کتابخانه همیشه منظم و خوانا عمل کنید. 
#define DS3231_SEC_REG 0x00 
#define DS3231_MIN_REG 0x01 
#define DS3231_HOUR_REG 0x02
#define DS3231_WDAY_REG 0x03
#define DS3231_MDAY_REG 0x04
#define DS3231_MONTH_REG 0x05
#define DS3231_YEAR_REG 0x06

#define DS3231_AL1SEC_REG 0x07
#define DS3231_AL1MIN_REG 0x08
#define DS3231_AL1HOUR_REG 0x09
#define DS3231_AL1WDAY_REG 0x0A

#define DS3231_AL2MIN_REG 0x0B
#define DS3231_AL2HOUR_REG 0x0C
#define DS3231_AL2WDAY_REG 0x0D

#define DS3231_CONTROL_REG 0x0E
#define DS3231_STATUS_REG 0x0F
#define DS3231_AGING_OFFSET_REG 0x10
#define DS3231_TMP_UP_REG 0x11
#define DS3231_TMP_LOW_REG 0x12

////////////////////////////////////////////////////////////////////////////////
// utility code, some of this could be exposed in the DateTime API if needed
در این بخش هم به نحوه راه‌اندازی دستورات ماژول و همینطور نحوه محاسبه زمان و تاریخ و 
همینطور محاسبه دما پرداخته شده است.
static const uint8_t daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };

// number of days since 2000/01/01, valid for 2001..2099
static uint16_t date2days(uint16_t y, uint8_t m, uint8_t d) {
if (y >= 2000)
y -= 2000;
uint16_t days = d;
for (uint8_t i = 1; i < m; ++i)
days += pgm_read_byte(daysInMonth + i - 1);
if (m > 2 && y % 4 == 0)
++days;
return days + 365 * y + (y + 3) / 4 - 1;
}

static uint32_t time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s) {
return ((days * 24L + h) * 60 + m) * 60 + s;
}

static uint8_t conv2d(const char* p) {
uint8_t v = 0;
if ('0' <= *p && *p <= '9')
v = *p - '0';
return 10 * v + *++p - '0';
}

////////////////////////////////////////////////////////////////////////////////
// DateTime implementation - ignores time zones and DST changes
// NOTE: also ignores leap seconds, see http://en.wikipedia.org/wiki/Leap_second

DateTime::DateTime (long t) {
ss = t % 60;
t /= 60;
mm = t % 60;
t /= 60;
hh = t % 24;
uint16_t days = t / 24;
uint8_t leap;
for (yOff = 0; ; ++yOff) {
leap = yOff % 4 == 0;
if (days < 365 + leap)
break;
days -= 365 + leap;
}
for (m = 1; ; ++m) {
uint8_t daysPerMonth = pgm_read_byte(daysInMonth + m - 1);
if (leap && m == 2)
++daysPerMonth;
if (days < daysPerMonth)
break;
days -= daysPerMonth;
}
d = days + 1;
wday = 0; // FIXME This is not properly initialized
}

DateTime::DateTime (uint16_t year, uint8_t month, uint8_t date, uint8_t hour, uint8_t min, uint8_t sec, uint8_t wd) {
if (year >= 2000)
year -= 2000;
yOff = year;
m = month;
d = date;
hh = hour;
mm = min;
ss = sec;
wday = wd;
}

// A convenient constructor for using "the compiler's time":
// DateTime now (__DATE__, __TIME__);
// NOTE: using PSTR would further reduce the RAM footprint
DateTime::DateTime (const char* date, const char* time) {
// sample input: date = "Dec 26 2009", time = "12:34:56"
yOff = conv2d(date + 9);
// Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec 
switch (date[0]) {
case 'J': m = date[1] == 'a' ? 1 : date[2] == 'n' ? 6 : 7; break;
case 'F': m = 2; break;
case 'A': m = date[2] == 'r' ? 4 : 8; break;
case 'M': m = date[2] == 'r' ? 3 : 5; break;
case 'S': m = 9; break;
case 'O': m = 10; break;
case 'N': m = 11; break;
case 'D': m = 12; break;
}
d = conv2d(date + 4);
hh = conv2d(time);
mm = conv2d(time + 3);
ss = conv2d(time + 6);
wday = 0; // FIXME This is not properly initialized
}

uint32_t DateTime::get() const {
uint16_t days = date2days(yOff, m, d);
return time2long(days, hh, mm, ss);
}

uint32_t DateTime::getEpoch() const
{
return get() + EPOCH_TIME_OFF;
}

/*
* Format an integer as %0*d
*
* Arduino formatting sucks.
*/
static void add0Nd(String &str, uint16_t val, size_t width)
{
if (width >= 5 && val < 1000) {
str += '0';
}
if (width >= 4 && val < 100) {
str += '0';
}
if (width >= 3 && val < 100) {
str += '0';
}
if (width >= 2 && val < 10) {
str += '0';
}
str += val;
}
static inline void add04d(String &str, uint16_t val) { add0Nd(str, val, 4); }
static inline void add02d(String &str, uint16_t val) { add0Nd(str, val, 2); }

void DateTime::addToString(String & str) const
{
add04d(str, year());
str += '-';
add02d(str, month());
str += '-';
add02d(str, date());
str += ' ';
add02d(str, hour());
str += ':';
add02d(str, minute());
str += ':';
add02d(str, second());
}

static uint8_t bcd2bin (uint8_t val) { return val - 6 * (val >> 4); }
static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); }

////////////////////////////////////////////////////////////////////////////////
// RTC DS3231 implementation

uint8_t Sodaq_DS3231::readRegister(uint8_t regaddress)
{
Wire.beginTransmission(DS3231_ADDRESS);
Wire.write((byte)regaddress);
Wire.endTransmission();

Wire.requestFrom(DS3231_ADDRESS, 1);
return Wire.read();
}

void Sodaq_DS3231::writeRegister(uint8_t regaddress,uint8_t value)
{
Wire.beginTransmission(DS3231_ADDRESS);
Wire.write((byte)regaddress);
Wire.write((byte)value);
Wire.endTransmission();
}

uint8_t Sodaq_DS3231::begin(void) {

unsigned char ctReg=0;

Wire.begin();
ctReg |= 0b00011100; 
writeRegister(DS3231_CONTROL_REG, ctReg); //CONTROL Register Address
delay(10);

// set the clock to 24hr format 
uint8_t hrReg = readRegister(DS3231_HOUR_REG);
hrReg &= 0b10111111;
writeRegister(DS3231_HOUR_REG, hrReg);

delay(10);

return 1; 
}

//set the time-date specified in DateTime format
//writing any non-existent time-data may interfere with normal operation of the RTC
void Sodaq_DS3231::setDateTime(const DateTime& dt) {

Wire.beginTransmission(DS3231_ADDRESS);
Wire.write((byte)DS3231_SEC_REG); //beginning from SEC Register address

Wire.write((byte)bin2bcd(dt.second())); 
Wire.write((byte)bin2bcd(dt.minute()));
Wire.write((byte)bin2bcd((dt.hour()) & 0b10111111)); //Make sure clock is still 24 Hour
Wire.write((byte)dt.dayOfWeek());
Wire.write((byte)bin2bcd(dt.date()));
Wire.write((byte)bin2bcd(dt.month()));
Wire.write((byte)bin2bcd(dt.year() - 2000)); 
Wire.endTransmission();

}

DateTime Sodaq_DS3231::makeDateTime(unsigned long t)
{
if (t < EPOCH_TIME_OFF)
return DateTime(0);
return DateTime(t - EPOCH_TIME_OFF);
}

// Set the RTC using timestamp (seconds since epoch)
void Sodaq_DS3231::setEpoch(uint32_t ts)
{
setDateTime(makeDateTime(ts));
}

//Read the current time-date and return it in DateTime format
DateTime Sodaq_DS3231::now() {
Wire.beginTransmission(DS3231_ADDRESS);
Wire.write((byte)0x00); 
Wire.endTransmission();

Wire.requestFrom(DS3231_ADDRESS, 8);
uint8_t ss = bcd2bin(Wire.read());
uint8_t mm = bcd2bin(Wire.read());

uint8_t hrreg = Wire.read();
uint8_t hh = bcd2bin((hrreg & ~0b11000000)); //Ignore 24 Hour bit

uint8_t wd = Wire.read();
uint8_t d = bcd2bin(Wire.read());
uint8_t m = bcd2bin(Wire.read());
uint16_t y = bcd2bin(Wire.read()) + 2000;

return DateTime (y, m, d, hh, mm, ss, wd);
}

//Enable periodic interrupt at /INT pin. Supports only the level interrupt
//for consistency with other /INT interrupts. All interrupts works like single-shot counter
//Use refreshINTA() to re-enable interrupt.
void Sodaq_DS3231::enableInterrupts(uint8_t periodicity)
{

unsigned char ctReg=0;
ctReg |= 0b00011101; 
writeRegister(DS3231_CONTROL_REG, ctReg); //CONTROL Register Address

switch(periodicity) 
{
case EverySecond:
writeRegister(DS3231_AL1SEC_REG, 0b10000000 ); //set AM1
writeRegister(DS3231_AL1MIN_REG, 0b10000000 ); //set AM2
writeRegister(DS3231_AL1HOUR_REG, 0b10000000 ); //set AM3
writeRegister(DS3231_AL1WDAY_REG, 0b10000000 ); //set AM4

break;

case EveryMinute:
writeRegister(DS3231_AL1SEC_REG, 0b00000000 ); //Clr AM1
writeRegister(DS3231_AL1MIN_REG, 0b10000000 ); //set AM2
writeRegister(DS3231_AL1HOUR_REG, 0b10000000 ); //set AM3
writeRegister(DS3231_AL1WDAY_REG, 0b10000000 ); //set AM4

break;

case EveryHour:
writeRegister(DS3231_AL1SEC_REG, 0b00000000 ); //Clr AM1
writeRegister(DS3231_AL1MIN_REG, 0b00000000 ); //Clr AM2
writeRegister(DS3231_AL1HOUR_REG, 0b10000000 ); //Set AM3
writeRegister(DS3231_AL1WDAY_REG, 0b10000000 ); //set AM4

break;
}
}

//Enable HH/MM/SS interrupt on /INTA pin. All interrupts works like single-shot counter
void Sodaq_DS3231::enableInterrupts(uint8_t hh24, uint8_t mm, uint8_t ss)
{
unsigned char ctReg=0;
ctReg |= 0b00011101; 
writeRegister(DS3231_CONTROL_REG, ctReg); //CONTROL Register Address

writeRegister(DS3231_AL1SEC_REG, 0b00000000 | bin2bcd(ss) ); //Clr AM1
writeRegister(DS3231_AL1MIN_REG, 0b00000000 | bin2bcd(mm)); //Clr AM2
writeRegister(DS3231_AL1HOUR_REG, (0b00000000 | (bin2bcd(hh24) & 0b10111111))); //Clr AM3
writeRegister(DS3231_AL1WDAY_REG, 0b10000000 ); //set AM4
}

//Disable Interrupts. This is equivalent to begin() method.
void Sodaq_DS3231::disableInterrupts()
{
begin(); //Restore to initial value.
}

//Clears the interrrupt flag in status register. 
//This is equivalent to preparing the DS3231 /INT pin to high for MCU to get ready for recognizing the next INT0 interrupt
void Sodaq_DS3231::clearINTStatus()
{
// Clear interrupt flag 
uint8_t statusReg = readRegister(DS3231_STATUS_REG);
statusReg &= 0b11111110;
writeRegister(DS3231_STATUS_REG, statusReg);

}

//force temperature sampling and converting to registers. If this function is not used the temperature is sampled once 64 Sec.
void Sodaq_DS3231::convertTemperature()
{
// Set CONV 
uint8_t ctReg = readRegister(DS3231_CONTROL_REG);
ctReg |= 0b00100000; 
writeRegister(DS3231_CONTROL_REG,ctReg); 


//wait until CONV is cleared. Indicates new temperature value is available in register.
do
{
//do nothing
} while ((readRegister(DS3231_CONTROL_REG) & 0b00100000) == 0b00100000 ); 

}

//Read the temperature value from the register and convert it into float (deg C)
float Sodaq_DS3231::getTemperature()
{
float fTemperatureCelsius;
uint8_t tUBYTE = readRegister(DS3231_TMP_UP_REG); //Two's complement form
uint8_t tLRBYTE = readRegister(DS3231_TMP_LOW_REG); //Fractional part

if(tUBYTE & 0b10000000) //check if -ve number
{
tUBYTE ^= 0b11111111; 
tUBYTE += 0x1;
fTemperatureCelsius = tUBYTE + ((tLRBYTE >> 6) * 0.25);
fTemperatureCelsius = fTemperatureCelsius * -1;
}
else
{
fTemperatureCelsius = tUBYTE + ((tLRBYTE >> 6) * 0.25); 
}

return (fTemperatureCelsius);

}

Sodaq_DS3231 rtc;

درواقع یکی‌از مزیت‌هایی که می‌تواند بررسی کتابخانه‌ها داشته‌باشد. نحوه اصولی کدنویسی است که خود می‌تواند بسیار در کدنویسی موثر واقع‌شود.

خب حالا نوبت‌به استفاده‌از این کتابخانه می‌رسد البته با این دید که می‌دانیم از چه توابعی استفاده‌شده و چطور مورداستفاده قرار گرفته‌شده‌است.

راه‌اندازی ماژول DS3231 با آردوینو

خب این بخش به دو قسمت شماتیک و کدنویسی تقسیم می‌شود.

شماتیک راه‌اندازی ماژول DS3231 با آردوینو
شماتیک برد آردوینو با DS3231
شماتیک برد آردوینو با DS3231

 

کدنویسی ماژول DS3231 با آردوینو

ابتدا باید کتابخانه مربوطه را نصب کنیم، که برای این کار باید طبق شکل‌هایی که در ادامه می‌گذاریم عمل نمایید.

 

حال پس‌از نصب کتابخانه نوبت به نوشتن کد موردنظر برای راه‌اندازی ماژول موردنظر است که در این قسمت می‌خواهیم از مثال‌های آماده این کتابخانه استفاده‌کنیم. برای این کار باید به‌صورت‌زیر عمل‌کنیم.

کدنویسی DS3231
کدنویسی DS3231

 

خب همونطوری‌که می‌بینید این کتابخانه دارای ۴نمونه کد راه‌انداز است که می‌خواهیم کد NOW را امتحان و درنهایت روی سریال مانیتور اطلاعات را نمایش‌دهیم.

کد مثال NOW

// Date and time functions using RX8025 RTC connected via I2C and Wire lib

#include <Wire.h>
#include "Sodaq_DS3231.h"

char weekDay[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };

void setup () 
{
Serial.begin(57600);
Wire.begin();
rtc.begin();
}

uint32_t old_ts;

void loop () 
{
DateTime now = rtc.now(); //get the current date-time
uint32_t ts = now.getEpoch();

if (old_ts == 0 || old_ts != ts) {
old_ts = ts;
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.date(), DEC);
Serial.print(' ');
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.print(' ');
Serial.print(weekDay[now.dayOfWeek()]);
Serial.println();
Serial.print("Seconds since Unix Epoch: "); 
Serial.print(ts, DEC);
Serial.println();
}
delay(1000);
}

 

درنهایت اگر تمامی مراحلی که توضیح دادیم به‌درستی انجام‌شده‌باشد باید خروجی سربال مانیتور مطابق اطلاعاتی که در ادامه قرار می‌دهیم، باشد.

2002/2/5 7:12:58 Wed
Seconds since Unix Epoch: 1012893178
2002/2/5 7:12:59 Wed
Seconds since Unix Epoch: 1012893179
2002/2/5 7:13:0 Wed
Seconds since Unix Epoch: 1012893180
2002/2/5 7:13:1 Wed
Seconds since Unix Epoch: 1012893181
2002/2/5 7:13:2 Wed
Seconds since Unix Epoch: 1012893182
2002/2/5 7:13:3 Wed
Seconds since Unix Epoch: 1012893183
2002/2/5 7:13:4 Wed
Seconds since Unix Epoch: 1012893184
2002/2/5 7:13:5 Wed
Seconds since Unix Epoch: 1012893185
2002/2/5 7:13:6 Wed
Seconds since Unix Epoch: 1012893186
2002/2/5 7:13:7 Wed
Seconds since Unix Epoch: 1012893187
2002/2/5 7:13:8 Wed
Seconds since Unix Epoch: 1012893188
2002/2/5 7:13:9 Wed
Seconds since Unix Epoch: 1012893189
2002/2/5 7:13:10 Wed
Seconds since Unix Epoch: 1012893190
2002/2/5 7:13:11 Wed
Seconds since Unix Epoch: 1012893191
2002/2/5 7:13:12 Wed

 

امیدوارم که این آموزش هم برای شما مفید واقع‌شده‌باشد.

 

منبع: سیسوگ

مطلب قبلیشاهکار جدید آردوینو (Chirp)
مطلب بعدیآموزش میکروبلیز قسمت سوم: آموزش زبان برنامه‌نویسی C

پاسخ دهید

لطفا نظر خود را وارد کنید!
لطفا نام خود را در اینجا وارد کنید