산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

Arduino를 사용한 정확한 시계

구성품 및 소모품

Arduino Nano R3
나노를 사용했지만 모든 Arduino와 함께 작동해야 합니다.
× 1
영숫자 LCD, 16 x 2
모든 디스플레이가 작동해야 합니다. 저는 https://www.amazon.co.uk/를 사용했습니다. gp/product/B00N8K2BYM/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1
× 1
촉각 스위치, 상단 작동
× 3
트리머 전위차계, 10kohm
어떤 10k 트리머도 가능
× 1
점퍼 와이어
× 1

이 프로젝트 정보

나는 이것을 학문적 운동으로 시작했지만 매우 정확한 시계로 끝났습니다. 5일 동안 러닝한 후에도 잃거나 얻은 적이 없습니다.

Arduino만 사용할 때의 주요 문제는 내부 클럭 속도가 100% 정확하지 않다는 것입니다. 따라서 이것에만 의존하면 경과된 밀리초 수가 작은 비율로 줄어들고 생성 중인 시계는 시간을 느슨하게 하거나 시간을 얻습니다. 내 접근 방식은 내가 사용하고 있던 Arduino의 정확도를 테스트하고 시간당 몇 밀리초가 손실되거나 증가했는지 확인하는 것이었습니다. 그때 필요한 모든 것은 내부적으로 추적된 매시간 밀리초에서 이 차이를 더하거나 빼도록 속도 조정을 프로그래밍하는 것이었습니다.

내 또 다른 우려는 Arduino 시계가 일관되게 부정확한지 여부였지만 표시된 대로 내가 프로그래밍한 시계는 5일 동안 매우 정확한 시간을 유지했기 때문에 부정확성이 일관되게 나타납니다.

두 번째 문제는 내부 millis() 함수가 약 50일마다 재설정되며 밀리초 수를 조작할 수 없다는 것입니다. 따라서 대답은 내가 조작할 수 있는 카운터를 사용하여 millis() 인터럽트를 교체하고 자정부터 밀리초를 계산하여 실행 시간 제한을 제거하는 매일 재설정하는 것이었습니다.

부정확성 평가

부정확성을 평가하기 위해 저는 제 컴퓨터 시계와 Processing의 millis()가 정확하다고 가정했습니다. 따라서 Arduino가 2초마다 한 번씩 핸드셰이킹 이후 경과된 밀리초 수를 Processing에 보내는 프로그램을 만들고 Processing이 이를 읽고 경과된 밀리초와 비교하여 실시간 결과와 한 시간 후의 차이를 표시하는 스크립트를 만들었습니다. 경과 이것은 한 시간 동안 손실 또는 증가된 밀리초 수를 제공하므로 시계 프로그램에서 속도 조정에 사용할 값입니다.

아두이노 프로그램의 코드와 처리 스크립트는 아래와 같습니다.

Processing이 설치되어 있지 않은 경우 https://processing.org를 방문하여 다운로드하여 자세히 알아볼 수 있습니다.

시계 코드

시계 코드의 주요 관심 영역은 인터럽트 설정, 이것이 사용되는 방식, 날짜가 유지되고 조작되는 방식입니다.

인터럽트

다음 코드는 밀리초마다 트리거되는 인터럽트를 설정합니다. 이것은 millis() 및 delay()가 더 이상 작동하지 않도록 millis()를 유지하는 데 사용되는 인터럽트를 전환합니다.

 // 시간 인터럽트 설정 - millis()는 50일 후에 롤오버되므로 // 우리는 매일의 끝에서 재설정할 수 있는 // 자체 밀리초 카운터를 사용하고 있습니다. //CTC 모드를 설정합니다. 비교 시간 및 트리거 인터럽트 TCCR0A =(1 < 

이것은 1초마다 호출될 코드입니다:

// 인터럽트는 비교 시간에 도달했을 때 호출되므로 // OCR0A 레지스터 설정에 따라 // 밀리초에 한 번 호출됩니다. ISR(TIMER0_COMPA_vect) { if (currentMode !=SET_TIME) 현재시간++; 경과++;}  

currentTime 및 elapsed는 부호 없는 긴 변수입니다. 메인 코드에서 변수를 조작할 때도 정의할 때 이러한 변수는 휘발성으로 규정됩니다. 이렇게 하면 시스템이 사용할 때마다 변수를 읽고 캐시된 값을 사용하지 않습니다.

currentTime은 자정 이후의 밀리초 수를 저장하며 이를 HH:MM:SS로 변환하고 시간을 설정할 때 재설정하는 루틴이 있습니다.

24시간이 경과하면 시스템은 시간에서 하루의 밀리초 수를 차감하고 날짜를 1일 증가시킵니다. 따라서 시계는 millis()와 달리 변수가 저장할 수 있는 최대값의 영향을 받지 않습니다.

 // 하루가 끝나면 시간을 재설정하고 날짜를 늘리면 if ((currentMode ==SHOW_TIME) &&(currentTime> millisecondsInADay)) { //다음 날 // 재설정 시간 동안 인터럽트 중지 noInterrupts(); 현재 시간 -=millisecondsInADay; 인터럽트(); 현재날짜++; } 

currentTime 변수를 조작하는 동안 인터럽트를 비활성화하지 않으면 계산 중간에 인터럽트 호출이 트리거되어 계산을 손상시키는 millisecondsInADay를 ​​공제할 수 있습니다.

매 시간이 지나면 시스템은 이전에 계산한 속도 조정에 의해 경과된 밀리초 수를 조정하고 빠르거나 느린 내부 클록을 보상하기 위해 현재 시간을 조정합니다.

 // 매 시간이 끝날 때 // Arduino 시계의 부정확성에 대한 경과 시간을 조정합니다. if (elapsed>=millisecondsInHour) { noInterrupts(); // 느린/빠른 실행 Arduino 클럭의 시간 조정 currentTime +=speedCorrection; // 다음 시간 경과를 계산하도록 재설정 =0; 인터럽트(); } 

날짜 저장 및 계산

날짜는 BC 4713년 1월 1일 월요일부터 경과된 일 수인 율리우스력 날짜로 유지됩니다. 율리우스력 날짜를 계산하고 다시 그레고리력으로 변환하는 루틴이 포함되어 있습니다.

float JulianDate(int iday, int imonth, int iyear) { // 율리우스력 날짜 계산(20,000년까지 테스트) unsigned long d =iday; 부호 없는 긴 m =imonth; unsigned long y =iyear; if (m <3) { m =m + 12; y =y - 1; } 부호 없는 긴 t1 =(153 * m - 457) / 5; 부호 없는 긴 t2 =365 * y + (y / 4) - (y / 100) + (y / 400); return 1721118.5 + d + t1 + t2;}void GregorianDate(float jd, int &iday, int &imonth, int &iyear) { // 참고 2100은 다음 건너뛴 윤년입니다. 건너뛴 윤년을 보상합니다. unsigned long f =jd + 68569.5; 부호 없는 긴 e =(4.0 * f) / 146097; 부호 없는 긴 g =f - (146097 * e + 3) / 4; 부호 없는 긴 h =4000ul * (g + 1) / 1461001; 부호 없는 긴 t =g - (1461 * h / 4) + 31; 부호 없는 긴 u =(80ul * t) / 2447; 부호 없는 긴 v =u / 11; iyear =100 * (e - 49) + h + v; imonth =u + 2 - 12 * v; iday =t - 2447 * u / 80;} 

조정 버튼

모드 버튼은 현재 모드를 시간 표시에서 시간 설정, 연도 설정, 날짜 설정, 속도 조정 설정으로 이동하고 다시 시간 표시로 이동합니다. 이들 각각은 설명이 필요 없으며 다른 2개의 버튼을 사용하여 현재 설정을 조정합니다.

시계가 작동 중일 때 시간이 증가하거나 감소하는 경우 속도 조정 설정 모드에 액세스하고 위/아래 버튼을 사용하여 한 번에 5초씩 증가 또는 감소시켜 속도 조정을 변경할 수 있습니다.

<섹션 클래스="섹션 컨테이너 섹션 축소 가능" id="코드">

코드

<울>
  • 시계 프로그램
  • Arduino 타이머 프로그램
  • 타이머 테스트 스크립트 처리
  • 시계 프로그램Arduino
    날짜가 포함된 정확한 시계 및 Arduino
    // Paul Brace - 2021년 2월// Arduino를 사용하여 만든 날짜가 있는 간단한 시계 - RTC 모듈 없음// 프로그램에 시간 수정 조정이 통합되어 내부// 클럭 속도 정확하지 않음 시간을 설정하려면// 모드 버튼(핀 2)은 시간 설정, 날짜 및 실행 설정을 토글합니다.// 버튼 1(핀 3)은 분 및 월을 증가시키고 년/속도 조정을 감소시킵니다.// 버튼 2(핀 4)는 시간 및 일을 증가시키고 증가 Year./speed adj// 24시간 표시// 표시용 라이브러리 드라이버 포함:#include // LiquidCrystal lcd( RS, EN, D4,D5, D6, D7)LiquidCrystal lcd(12, 13 , 6, 7, 8, 9); // LCD 객체를 생성하고 핀을 할당합니다.// 버튼과 버저 연결을 정의합니다.#define MODE_BUTTON 2#define HOUR_BUTTON 3 // 같은 버튼을 다른 정의로 ​​정의합니다.#define UP_BUTTON 3 // 코드를 이해하기 쉽게 만듭니다. #define DAY_BUTTON 3#define MINUTE_BUTTON 4 // 같은 버튼은 #define DOWN_BUTTON 4 // 코드를 이해하기 쉽게 만듭니다. #define MONTH_BUTTON 4// 현재 모드 설정#define SHOW_TIME 1 // 1 =실행 중 - 표시 시간#define SET_TIME 2 // 2 =시간 설정 #define SET_YEAR 3 // 3 =연도 설정#define SET_DATE 4 // 4 =일/월 설정 #define SET_SPEED_ADJ 5 // 5 =speedCorrection 변수 수정int speedCorrection =3545; // 내 Nano 시계가 시간당 느리게 실행되는 시간(밀리초)// 빠르게 실행 중인 경우 여기에 음수// Arduino와 일치하도록 변경// 인터럽트에서 변경된 휘발성 변수 및 우리는// 시스템이 읽기를 강제로 수행해야 합니다. 실제 변수 // 인터럽트 외부에서 사용되고 캐시된 버전을 사용하지 않는 경우 volatile unsigned long currentTime; // 자정부터 밀리초 단위의 기간 unsigned long lastTime =-1000; // ShowTime이 호출된 lastTime은 -1000으로 초기화되어 즉시 휘발성 unsigned long elapsed를 표시합니다. // 지연 및 시간 계산에 사용되는 타이머 unsigned long millisecondsInADay; // 24시간의 밀리초 unsigned long millisecondsInHour; // 1시간의 밀리초 int currentMode; // 1 =실행 - 시간 표시// 2 =설정된 시간// 3 =설정된 연도// 4 =일/월 setfloat currentDate; // 율리우스력 datefloat lastDate =0.0; // ShowDate가 호출된 마지막 날짜int currentDay;int currentMonth;int currentYear;char *dayArray[] ={ "Tue. ", // 컴파일러 경고를 표시하지만 잘 작동합니다. "Wed. ", "Thur. ", "Fri . ", "토요일", "일요일", "월요일" };void setup() { // 시간 인터럽트 설정 - millis()는 50일 후에 롤오버되므로 // 우리는 자체 밀리초 카운터를 사용하고 있습니다. // 매일 끝에서 재설정할 수 있습니다. TCCR0A =(1 < millisecondsInADay)) { //Next day // 리셋 시간 동안 인터럽트 중지 noInterrupts(); 현재 시간 -=millisecondsInADay; 인터럽트(); 현재날짜++; } // 매 시간이 끝날 때 // Arduino 시계의 부정확성을 위해 경과 시간을 조정합니다. if (elapsed>=millisecondsInHour) { noInterrupts(); // 느린/빠른 실행 Arduino 클럭의 시간 조정 currentTime +=speedCorrection; // 다음 시간 경과를 계산하도록 재설정 =0; 인터럽트(); } // 눌려진 버튼이 있는지 확인 CheckButtons(); // 현재 모드를 기반으로 디스플레이 표시 switch (currentMode) { case SHOW_TIME:// 현재 시간 및 날짜 표시 ShowTime(currentTime); 날짜 표시(현재 날짜); 부서지다; case SET_TIME:// 시간 설정 화면 표시 ShowTimeSet(currentTime); 부서지다; case SET_YEAR:// 연도 설정 화면 표시 ShowYearSet(currentDate); 부서지다; case SET_DATE:// 요일과 월을 설정하는 화면 표시 ShowDDMMSet(currentDate); 부서지다; case SET_SPEED_ADJ:// 속도 보정을 위한 표시 화면 ShowSpeedSet(); 부서지다; } Wait(150);}// 비교 시간에 도달하면 인터럽트가 호출됩니다. // 따라서 OCR0A 레지스터 설정을 기반으로 // 밀리초에 한 번 호출됩니다. ISR(TIMER0_COMPA_vect) { if (currentMode !=SET_TIME ) 현재 시간++; elapsed++;}float JulianDate(int iday, int imonth, int iyear) { // 율리우스력 날짜 계산(20,000년까지 테스트) unsigned long d =iday; 부호 없는 긴 m =imonth; unsigned long y =iyear; if (m <3) { m =m + 12; y =y - 1; } 부호 없는 긴 t1 =(153 * m - 457) / 5; 부호 없는 긴 t2 =365 * y + (y / 4) - (y / 100) + (y / 400); return 1721118.5 + d + t1 + t2;}void GregorianDate(float jd, int &iday, int &imonth, int &iyear) { // 참고 2100은 다음 건너뛴 윤년입니다. 건너뛴 윤년을 보상합니다. unsigned long f =jd + 68569.5; 부호 없는 긴 e =(4.0 * f) / 146097; 부호 없는 긴 g =f - (146097 * e + 3) / 4; 부호 없는 긴 h =4000ul * (g + 1) / 1461001; 부호 없는 긴 t =g - (1461 * h / 4) + 31; 부호 없는 긴 u =(80ul * t) / 2447; 부호 없는 긴 v =u / 11; iyear =100 * (e - 49) + h + v; imonth =u + 2 - 12 * v; iday =t - 2447 * u / 80;}void SplitTime(unsigned long curr, unsigned long &ulHour, unsigned long &ulMin, unsigned long &ulSec) { // 밀리초 카운트에서 HH:MM:SS 계산 ulSec =curr / 1000; ulMin =ulSec / 60; ulHour =ulMin / 60; ulMin -=ulHour * 60; ulSec =ulSec - ulMin * 60 - ulHour * 3600;}unsigned long SetTime(unsigned long ulHour, unsigned long ulMin, unsigned long ulSec) { // 자정부터 현재 시간까지의 밀리초 수 설정 return (ulHour * 60 * 60 * 1000) + (ulMin * 60 * 1000) + (ulSec * 1000);}void Wait(unsigned long value) { // 자체 거래 함수 생성 // TCCR0A에 자체 인터럽트를 설정했습니다 // 따라서 millis() 및 delay()는 더 이상 unsigned long startTime =elapsed를 작동하지 않습니다. while ((elapsed - startTime)  12) { iMonth =1; } // 현재 설정에 따라 저장 날짜 설정 currentDate =JulianDate(iDay, iMonth, iYear); } if (digitalRead(DAY_BUTTON) ==LOW) { // 전진일 int iDay; int iMonth; int iYear; GregorianDate(현재 날짜, iDay, iMonth, iYear); 아이데이++; if (iDay> 31) { iDay =1; } if (((iMonth ==4) || (iMonth ==6) || (iMonth ==9) || (iMonth ==11)) &&(iDay> 30)) { iDay =1; } if ((iMonth ==2) &&(iDay> 29)) { iDay =1; } if ((iMonth ==2) &&((iYear % 4) !=0) &&(iDay> 28)) { iDay =1; } // 현재 설정을 기반으로 저장된 날짜 설정 // 이후에 월을 조정하면 날짜가 유효하지 않습니다. // 표시는 다음 유효한 날짜로 진행됩니다. currentDate =JulianDate(iDay, iMonth, iYear); } 부서지다; case SET_SPEED_ADJ:// 보정을 5밀리초씩 늘리거나 줄입니다. if (digitalRead(UP_BUTTON) ==LOW) { speedCorrection +=5; } if (digitalRead(DOWN_BUTTON) ==LOW) { speedCorrection -=5; } 부서지다; } }}String FormatNumber(int value) { // 필요한 경우 선행 0을 추가하려면 if (value <10) { return "0" + String(value); } else { 반환 문자열(값); }}void ShowTime(unsigned long value) { // 1초에 한 번 디스플레이 업데이트 // 또는 자정이 넘어갈 때 if ((value> lastTime + 1000) || (value  
    Arduino 타이머 프로그램Arduino
    이 프로그램은 경과된 밀리초 수를 직렬 포트로 보낸다. 2초.
    // Paul Brace Feb 2021// 해당 처리 스크립트와 함께 사용// 여기에서 millis()와 비교하기 위해 // Processing 컴퓨터 사용 clockint inByte =0; unsigned long firstReading =100000; // 처음 읽을 때 millis() sentvoid setup() { Serial.begin(9600); // Hello 바이트를 Processing으로 보냅니다. sayHello();}void loop() { // 직렬 포트에서 바이트를 받으면 // 읽고 버리고 현재 // millis() 값을 보냅니다. if (Serial. available()> 0){ // 들어오는 바이트를 얻습니다. inByte =Serial.read(); // 첫 번째 읽기 처리 이후 경과된 시간을 보냅니다. Serial.print(millis() - firstReading); Serial.print('E'); // 2초마다 반복합니다. delay(2000); }}void sayHello(){ // 직렬 포트를 사용할 수 있을 때까지 기다렸다가 // Hello 바이트를 전송하여 핸드셰이크를 시작합니다. while (Serial.available() <=0){ Serial.print('Z'); // Hello라고 말하도록 처리에 Z를 보냅니다. delay(200); } firstReading =millis();}
    타이머 테스트 스크립트 처리처리 중
    Arduino에서 보낸 밀리초를 읽고 처리 중인 경과된 밀리초와 비교하는 Processing용 스크립트입니다.
    // Paul Brace Feb 2021// Arduino에서 millis()를 받아 다음과 비교하는 스크립트입니다. 내부 millis() to// Arduino 시계의 부정확성을 평가합니다.// 컴퓨터 시계가 정확하다고 가정합니다.// -ve =Arduino가 느리게 실행되고 있으므로 시계 프로그램에서 +ve 조정으로 입력// +ve =Arduino는 빠르게 실행되므로 클럭을 낮추기 위해 -ve 조정으로 입력하십시오.import processing.serial.*;SerialSerialPort; // 직렬 포트 생성 objectint[] serialBytesArray =new int[15]; // 들어오는 바이트열을 저장할 배열int bytesCount =0; // 수신된 현재 바이트 수boolean init =false; // 문자를 수신하여 핸드셰이크가 완료될 때까지 거짓 Zint fillColor =255; // 초기 채우기 정의 colorlong mills =0; // 마지막으로 읽은 값long first =0; // 한 시간 동안의 차이를 계산할 수 있도록 수신된 첫 번째 밀의 시간; // 첫 번째 밀 수신 이후 경과된 밀리 수long firstReading =100000; // 아두이노로부터 받은 첫 번째 메시지 처리 중 millis() DiffPerHour =0; // 첫 1시간 경과 후의 차이 int inByte; // 마지막 바이트 readvoid setup() { // 일부 캔버스 및 그리기 매개변수 정의 size(500, 500); 배경(70); 노스트로크(); // 모든 직렬 장치 목록을 인쇄하여 Arduino에 설정할 장치를 알 수 있습니다. // 올바른 포트가 아래에 설정되지 않은 경우 프로그램을 실행하고 편집해야 합니다. printArray(Serial.list()); // 직렬 통신 문자열을 인스턴스화합니다. thePortName =Serial.list()[1]; theSerialPort =new Serial(this, thePortName, 9600);}void draw() { // 디스플레이 시간 설정 background(70); 채우기(채우기 색상); 텍스트 크기(25); 텍스트(시() + ":" + 분() + ":" + 초(), 50, 50); // Arduino에서 보낸 마지막 읽기 millis text("Incoming elapsed:" + mills, 50, 100); // Processing에서 처음 읽은 이후 경과된 현재 text("Local elapsed:" + (now - firstReading), 50, 150); // 현재 차이를 표시합니다. text("Diff:" + (mills - (현재 - firstReading)), 50, 200); // 1시간이 지났는지 확인하고 첫 시간이 차이를 저장하는지 확인 if (((now - firstReading)>=3600000) &&(DiffPerHour ==0)){ DiffPerHour =mills - (now - firstReading); } // 첫 번째 차이와 첫 번째 시간 이후의 차이 표시 text("Diff after 1 hour:" + DiffPerHour, 50, 300);}void serialEvent(Serial myPort) { // 직렬 포트에서 바이트 읽기 inByte =myPort.read(); if (init ==false) { // 아직 핸드셰이크되지 않은 경우 핸드셰이크 확인 확인 byte if (inByte =='Z') { // 읽은 바이트가 Z인 경우 myPort.clear(); // 직렬 포트 버퍼 지우기 init =true; // 첫 번째 hello가 있다는 사실을 저장합니다. myPort.write('Z'); // Arduino에게 더 많은 것을 보내도록 지시 if (first ==0){ first =millis(); } } } else { // 첫 번째 hello가 이미 있는 경우 // 직렬 포트의 최신 바이트를 배열에 추가 if (inByte !=69) { // 메시지 문자 E의 끝을 확인하지 않음 if (bytesCount <14) { 직렬바이트 배열[bytesCount] =inByte; 바이트 수++; } } if (inByte ==69) { // 메시지 끝 // 현재 경과된 현지 시간 저장 =millis(); // 들어오는 millis()를 계산합니다. mills =0; for (int i =1; i <=bytesCount; i++) { mills +=(serialBytesArray[i - 1] - 48) * pow(10, (bytesCount - i)); } // 다음 메시지를 받을 준비가 되었다고 가정합니다. // 이것이 첫 번째 판독이면 첫 번째 차이를 설정합니다. if (firstReading ==100000) { firstReading =now; } myPort.write('Z'); // bytesCount 재설정:bytesCount =0; } }}

    회로도


    제조공정

    1. Arduino 관점 시계
    2. Arduino만 사용하는 DTMF 디코더
    3. Arduino를 사용하여 모니터 Ambilight 만들기
    4. Adafruit 1/4 60 Ring Neopixel을 사용한 간단한 벽시계
    5. 단순 워드 클럭(Arduino)
    6. 이슬람 기도 시간이 있는 Arduino 시계
    7. 마스터 시계
    8. Arduino와 스마트폰을 사용한 DIY 전압계
    9. IoT를 사용한 심박수 모니터
    10. Arduino Uno WiFi를 사용하는 WebServerBlink