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

0
1326
راه اندازی ماژول DS3231
راه اندازی ماژول DS3231

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

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

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

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

منبع ویدئو: یوتیوب

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

  • تنظیم سال
  • تنظیم ماه
  • تنظیم روز
  • تنظیم روزهای هفته
  • تنظیم ساعت
  • تنظیم دقیقه
  • تنظیم ثانیه
  • داشتن یک سنسور داخلی که قابلیت اندازه گیری دما از -40 تا 85 درجه سانتی‌گراد را دارد.

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

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

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

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

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

نحوه صحیح دیتاشیت خوانی  و راه اندازی ماژول  DS3231

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

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

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

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

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

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

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

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

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

دستورات ماژول DS3231

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

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

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

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

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

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

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

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

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

نصب کتابخانه برای کدنویسی ماژول DS3231 با آردوینو
نصب کتابخانه برای کدنویسی ماژول DS3231 با آردوینو

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

نصب کتابخانه برای کدنویسی ماژول DS3231 با آردوینو

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

DS3231 Arduino library

#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;

Sample Code

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

Project Output

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

منبع: وبسایت بعثت الکترونیک

مطلب قبلیراه اندازی آیسی Max7219
مطلب بعدیPWM چیست؟

پاسخ دهید

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