這篇要介紹一個好用的Arduino計時器(Timer)程式庫,由Dr. Monk所開發,由Jack Christensen稍加修改後放在github上。
下載並解壓縮後,把目錄改名成Timer,搬移到Arduino軟體開發環境的sketchbook目錄的libraries子目錄下,你可以從File-Preferences-Sketchbook location:查到sketchbook目錄在哪,若裡面沒有libraries這個子目錄,請自行建立,然後把計時器程式庫整個Timer目錄搬進去。
需要重新開啟Arduino軟體開發環境,它才知道你新安裝的程式庫。
先舉個例子,假設要閃爍一個LED,點亮1秒,熄滅1秒,不斷循環,應該很簡單(這通常會是學習Arduino的第一支程式),程式碼如下:
假設Arduino腳位2接到LED的長腳(中間串接220 ohm電阻),短腳接地。
void setup() {
pinMode(2, OUTPUT);
}
void loop() {
digitalWrite(2, HIGH);
delay(1000);
digitalWrite(2, LOW);
delay(1000);
}
好,那接下來,如果腳位3、4、5也都接了LED,我們想要讓每個LED以不同頻率閃爍,那該怎麼寫呢。
unsigned long previous_time;
void setup() {
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
digitalWrite(2, HIGH);
digitalWrite(3, HIGH);
digitalWrite(4, HIGH);
digitalWrite(5, HIGH);
previous_time = millis();
}
void update2(unsigned long time_passed){
static unsigned long time = 0;
time += time_passed;
if((time / 200) % 2 == 0){ // 腳位2的LED明滅頻率為0.2秒
digitalWrite(2, HIGH);
}
else{
digitalWrite(2, LOW);
}
}
void update3(unsigned long time_passed){
static unsigned long time = 0;
time += time_passed;
if((time / 300) % 2 == 0){ // 腳位3的LED明滅頻率為0.3秒
digitalWrite(3, HIGH);
}
else{
digitalWrite(3, LOW);
}
}
void update4(unsigned long time_passed){
static unsigned long time = 0;
time += time_passed;
if((time / 400) % 2 == 0){ // 腳位4的LED明滅頻率為0.4秒
digitalWrite(4, HIGH);
}
else{
digitalWrite(4, LOW);
}
}
void update5(unsigned long time_passed){
static unsigned long time = 0;
time += time_passed;
if((time / 500) % 2 == 0){ // 腳位5的LED明滅頻率為0.5秒
digitalWrite(5, HIGH);
}
else{
digitalWrite(5, LOW);
}
}
void loop() {
unsigned long current_time = millis();
unsigned long time_passed;
if(current_time > previous_time ){
time_passed = current_time - previous_time;
}
else{
time_passed = ULONG_MAX - previous_time + current_time;
}
if(time_passed >= 100){
update2(time_passed);
update3(time_passed);
update4(time_passed);
update5(time_passed);
previous_time = current_time;
}
}
每次在loop裡,檢查距離上次更新是否經過了100ms(0.1秒),若是,呼叫update2、update3、update4、update5四個函式,分別負責更新腳位2、3、4、5的狀態。
你可以看得出來,有越多東西要處理,程式就會變得越複雜。若是有Timer計時器程式庫的話,就會變得簡單許多。
改用計時器後的程式碼如下:
// 首先匯入程式庫標頭檔Timer.h
#include <Timer.h>
// 用四個計時器控制四個LED
Timer t2;
Timer t3;
Timer t4;
Timer t5;
void setup() {
pinMode(2, OUTPUT); // 腳位設定為輸出模式
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
t2.oscillate(2, 200, HIGH); // 以oscillate設定每幾毫秒切換一次狀態
t3.oscillate(3, 300, HIGH); // 第一個參數:哪個腳位
t4.oscillate(4, 400, HIGH); // 第二個參數:幾毫秒
t5.oscillate(5, 500, HIGH); // 第三個參數:初始狀態
}
void loop() {
// 在loop裡,呼叫每個計時器的update,它才能運作更新狀態
t2.update();
t3.update();
t4.update();
t5.update();
}
如何,是不是簡單許多、合理許多了呢。
還有個every方法,每經過一段時間,就呼叫某回呼函式。範例:
#include <Timer.h>
Timer tcb;
void writeToSerial(){
static unsigned long count = 0;
Serial.println(count); // 從0開始輸出,每次加1
count++;
}
void setup() {
Serial.begin(115200);
tcb.every(1000, writeToSerial); // 每經過1000毫秒,就會呼叫writeToSerial
}
void loop() {
tcb.update();
}
其他功能還有after(經過一段時間後,呼叫某函式,只呼叫一次)、pulse(經過一段時間後,切換某腳位的狀態,只一次)、stop(取消某個計時器事件)、等等,可看看原文章的最下面,有API解說。
PS 另外有個計時器程式庫叫Metro,不過我覺得比較不好用。
我使用你的every來跑count,然後我顯示在LCD上的圖片就會突然的消失
ReplyDelete然後砍掉那些Timer的程式碼,就又出現了==
呃,哪裡出錯了嗎?
DeleteThis comment has been removed by the author.
Delete如果使用every那要怎停止它,我寫tcb.stop就失敗==“
ReplyDelete呼叫every時會回傳計時器事件ID,之後要停止時須傳入stop。
Delete我不曉得要怎麼stop....
ReplyDelete呼叫every時會回傳計時器事件ID,必須記住,
Delete之後想要停止時,呼叫stop並傳入該ID。
還有人回嗎?請教.stop的ID要記住??這句話我實在不懂
Delete一個Timer可以控制好幾個事件,
Delete每次呼叫every安排新的事件,every會回傳該事件的ID,
當你要stop時,須傳入ID,這樣才知道你想stop哪個事件。
#include "Timer.h"
DeleteTimer t;
int ledEvent;
void setup(){
Serial.begin(9600);
pinMode(13, OUTPUT);
ledEvent = t.oscillate(13, 50, HIGH);
}
void loop(){
if(Serial.available()>0){
char c = Serial.read();
if(c == '1'){
t.stop(ledEvent);
}
}
t.update();
}
你好~我想寫一個程式類似time的功能但是我的想法是
ReplyDelete程式開使跑的時候開始計時 一直到程式結束(或者程式中間)時停止
我想知道總共多少時間 想要顯示出來
用millis()不就可以得知微控制器重置後經過多少時間嗎?
Delete然後再呼叫一次,兩者相減,便可得知相差多少時間。
是大概像下面那樣寫嗎?
ReplyDelete#include "Timer.h"
int t = 0;
int t1 = 0;
int t2 = 0;
void setup()
{
t = millis() ;
.
.
.
.
t1 = millis() ;
t2 = t1 - t ;
Serial.print("時間:");
Serial.println(t2, HEX);
}
millis()回傳的是unsigned long,
Deletemillis()是內建函式,不是Timer程式庫裡的東西。
請問使用
ReplyDelete裡面的Timer用完了,可以呼叫出另外一個Timer2嗎??
在請問一下Arduino TFT板上面的LCD CS, SD CS, RESET的線可以設定到其他pin腳位上嗎??
Delete可以建立多個Timer物件,
DeleteTimer t1;
Timer t2;
等等。
某個Timer物件若想更動設定,可先呼叫stop,但須傳入當初取得的id;然後再呼叫every或oscillate或其他設定方法。
我沒有Arduino TFT板。
Delete看http://arduino.cc/en/Guide/TFT的介紹,應該可以把你說的腳位連接到其他pin,同時也須修改程式碼裡的腳位定義。
葉大 查了很多網站在LOOP裡
ReplyDelete點亮10秒熄滅,不循環
都查不到怎麼寫
有沒有甚麼方法可以用?
使用Timer程式庫的
Deleteint every(long period, callback, int repeatCount)
period傳入10000,
callback回呼函式裡把LED熄滅,
repeatCount傳入1,
應該就可以達到你的要求。
請問我想要一段時間後讓arduino重置(reset pin 接 12 pin)
ReplyDelete用 every/after/pulse 哪個比較好?
嗯,after吧。
Delete你好, 我目前製作一個簡單的電路,用作讀取三軸加速規的訊號。目前我使用arduino uno 做三軸加速規的類比數位轉換。在類比轉換之前我加入了三組自行設計的5階濾波器在加速規的輸出端,在訊號做類比轉換前能濾掉高頻雜訊。現在我希望能加入一個4對1的多工器,在加速規及濾波器之間做切換,如此一來只需要一組濾波器。在多工器的控制方面我是使用aradino的PORT B 最後兩個腳位做00,01,10的變化來做時序控制,日前參考你這篇的counter來撰寫多工器的時序控制,同時做AD轉換,但是我碰到了一些問題,還望你能指教。
ReplyDelete為了不在類比轉換時做多工器的切換,我是使用every來撰寫每隔100ms,時序旗標會變成1,PORTB會加1,進而可以切換多工器,但我發現我的程式在進入loop後,雖然有寫t.update();
但是不會進入我所寫的每隔100ms所要呼叫的程式,導致無法做多工器的切換。
不知哪裡修改後就能work呢? 還望指點, 謝謝。
詳細程式碼如下:
#include "Timer.h"
Timer t;
int acc = A1;
int timeflag;
int count;
void mulswitch()
{
int timeflag = 1;
count++;
}
void setup()
{
Serial.begin(9600);
PORTB = 000000;
timeflag = 0;
t.every(100,mulswitch);
}
void loop()
{
t.update();
while (timeflag != 1)
{
if (PORTB == 000000)
{
Serial.println(analogRead(acc));
Serial.print("AccX\t");
}
else if (PORTB == 000001)
{
Serial.print(analogRead(acc));
Serial.print("AccY\t");
}
else if (PORTB ==000010)
{
Serial.print(analogRead(acc));
Serial.print("AccZ\n");
}
}
while (timeflag == 1)
{
PORTB++;
timeflag = timeflag - 1;
while (PORTB == 2)
{
PORTB = PORTB - 2;
}
}
}
在setup裡timeflag = 0;
Delete然後在loop裡
while (timeflag != 1)
{
// ...
}
timeflag != 1永遠為真,所以while迴圈不斷執行,這是無窮迴圈。
在setup裡面寫timeflag = 0; 不是表示初始值為0嗎?
Delete還是我應該在宣告的地方寫 int timeflag = 0呢?
問題在於造成無窮迴圈。
Delete大致上,應要把while改成if,至於細節要自己推敲。
您好
ReplyDelete如果我要讓arduino每10分鐘執行某個動作
但是又不影響其他程式碼的運行
請問是否能用此程式庫
謝謝您
可以,大致上作法如下:
Delete把動作獨立出來放在某個函式裡,
建立Timer物件,呼叫every,參數是10*60(秒)與該函式,
然後在loop裡上述Timer物件的update。
我成功囉!!!
Delete謝謝您
Congratulations
Delete請問一下我如果要讓伺服馬達從0轉180度在從180度轉為0~~這一個動作每10分鐘執行一次~~該怎麼用
Delete#include
Servo myservo;
void setup()
{
myservo.attach(9);
}
void loop()
{
for(int i = 0; i <= 180; i+=1){
myservo.write(i); //
delay(20);
}
for(int i = 180; i >= 0; i-=1){
myservo.write(i);
delay(20);
}
}
在loop最後面加上delay(10 * 60 * 1000);
DeleteServo myservo;
Deletevoid setup()
{
myservo.attach(9);
}
void loop()
{
for(int i = 0; i <= 180; i+=1){
myservo.write(i); //
delay(20);
}
for(int i = 180; i >= 0; i-=1){
myservo.write(i);
delay(20);
}
delay(10 * 60 * 1000);
}
請問是這樣嗎??
呃,試試看不就知道了。
Delete有試了~~但是還是一樣呢
Delete啊,抱歉,忘了int只有2 bytes。
Delete10 * 60 * 1000會變成10秒左右,
請改成10 * 60 * 1000L
請問 如果要讓arduino動作10分鐘然後停止 停止之後再用一個觸發鍵來重新啟動
ReplyDelete要怎麼用timer做
目前我用arduino睡眠模式做
但他只能控制一個時間
不能控制多個時間
> 停止之後再用一個觸發鍵來重新啟動
Delete這個就用睡眠模式來做。
> 讓arduino動作10分鐘然後停止
可以自己用millis()計算時間,或是用軟體計時器程式庫來做,譬如https://github.com/JChristensen/Timer
假如我想讓我的analog read每10ms抓一次data到serial print,還有我的電壓峰對峰值每一秒抓一次data
ReplyDelete我跑了一次發現一秒鐘之內是會在serial monitor上顯示10ms 的data, 可是過一秒鐘之後就無法回到我的analog voltage
就只能變成一秒鐘顯示一次 不知道是哪出了問題 謝謝
#include
Timer tcb;
Timer tcb2;
void analog(){
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage = sensorValue * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage);
}
void vpp() {
const int sampleWindow = 1000; // Sample window width in mS
unsigned int sample;
const int sensorIn = A0;
unsigned long startMillis= millis(); // Start of sample window
unsigned int peakToPeak = 0; // peak-to-peak level
unsigned int signalMax = 0;
unsigned int signalMin = 1024;
// collect data for 1S
while (millis() - startMillis < sampleWindow)
{
sample = analogRead(sensorIn);
if (sample < 1024) // toss out spurious readings
{
if (sample > signalMax)
{
signalMax = sample; // save just the max levels
}
else if (sample < signalMin)
{
signalMin = sample; // save just the min levels
}
}
}
peakToPeak = signalMax - signalMin; // max - min = peak-peak amplitude
double volts = (peakToPeak * 5.0) / 1024.0; // convert to volts
Serial.println(volts);
}
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
tcb.every(1000,vpp);
tcb2.every(10,analog);
}
// the loop routine runs over and over again forever:
void loop() {
tcb.update();
tcb2.update();
}
因為你在vpp()裡使用while、millis、sampleWindow,又拖了一秒。因此影響analog()。
Delete建議改法:
analog()裡讀取A0,並且記錄signalMax與signalMin。
vpp()裡就只要算出peakToPeak與volts即可,並且把signalMax與signalMin重置為某特定值。
不好意思 把signalMax與signalMin重置為某特定值是甚麼意思 謝謝
Delete能夠再被
Deleteif (sample > signalMax)
{
signalMax = sample; // save just the max levels
}
else if (sample < signalMin)
{
signalMin = sample; // save just the min levels
}
處理的值。
譬如把signalMax重置為-9999,把signalMin重置為9999。
不好意思,我現在正在製作偵測紫外線相關的裝置,遇到困難,想請問如果我想累積劑量(將a0轉換後對照出之級數乘上照射時間),應該如何攥寫我的程式碼 ?(比如說現在6級的時候照了15分鐘,8級照了10分鐘,我應該如何將我的時間在跳級的時候及時重新計算?該如何使用timer?)謝謝你😬
ReplyDelete嗯,聽起來不需要這一篇介紹的timer吧。
Delete就不斷地偵測紫外線劑量a0,得知是幾級,同時也會看時間,
「一旦跳級時」,就記錄劑量與時間,然後繼續偵測接下來的劑量與時間。
This comment has been removed by the author.
ReplyDelete謝謝你��
ReplyDelete但是我現在已經可以偵測到級數了跳級也可以馬上偵測到 但是不知道如何加入乘上時間累積劑量這個功能(因為想在到達一定的劑量時發出警報)
code?
Delete這是我一部分的程式碼
Delete忘了說我來有用lcd(16x2)來顯示
數據那些都孩是我亂設定的(但是每一級都是對應的A0輸出都有一個特定範圍)
現在就是不知道應該如何加入累積劑量 時間還有緊報 這塊
救救我吧~~~plz~~~~乾蝦哩~~~~
#include
LiquidCrystal lcd(7,6,5,4,3,2);
int sensorPin = A0;
int sensorValue = 0;
void setup()
{
Serial.begin(9600);
lcd.begin(16, 2);
lcd.setCursor(5,0);
lcd.print("HELLO");
lcd.setCursor(4,1);
lcd.print("WELCOME");
delay(1000);
}
void loop()
{
sensorValue = analogRead(sensorPin);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("NO SUNSCREAM");
lcd.setCursor(0,1);
lcd.print("A0:");
lcd.setCursor(3,1);
lcd.print(sensorValue);
lcd.setCursor(8,1);
lcd.print("Lvl:");
if(sensorValue<=93){
lcd.setCursor(13,1);
lcd.print("1");
}
else if(sensorValue<=186){
lcd.setCursor(13,1);
lcd.print("2");
}
else if(sensorValue<=297){
lcd.setCursor(13,1);
lcd.print("3");
}
else if(sensorValue<=372){
lcd.setCursor(13,1);
lcd.print("4");
}
else if(sensorValue<=465){
lcd.setCursor(13,1);
lcd.print("5");
}
else if(sensorValue<=558){
lcd.setCursor(13,1);
lcd.print("6");
}
else if(sensorValue<=651){
lcd.setCursor(13,1);
lcd.print("7");
}
else if(sensorValue<=744){
lcd.setCursor(13,1);
lcd.print("8");
}
else if(sensorValue<=837){
lcd.setCursor(13,1);
lcd.print("9");
}
else if(sensorValue<=930){
lcd.setCursor(13,1);
lcd.print("10");
}
else if(sensorValue<=1023){
lcd.setCursor(13,1);
lcd.print("11");
}
delay(1000);
}
呼叫millis(),得知時間。宣告全域變數,記錄之前的強度與照射時間。
Delete累積劑量不知道怎麼算。
大概是這樣吧:
unsigned long v_old; 代表上次偵測到的強度
unsigned long t_old; 代表偵測到上次強度的最初時間
unsigned total; 累積劑量
void setup(){
total = 0;
v_old = 1; 假設上次強度是1
t_old = millis();
}
void loop(){
偵測強度
若強度與上次強度相同,不做事。
若不同,就更新累積劑量
total = v_old * ((millis() - t_old)/1000); 假定累積劑量是強度乘上時間
更新強度 v_old = 新強度
更新偵測到新強度的起始時間 t_old = millis()
}
大概是這樣吧,沒仔細看過也沒側試過。請斟酌採用。
謝謝你~但是我還是寫不出來嗚嗚
Delete請問這應該要怎麼寫
若強度與上次強度相同,不做事。
若不同,就更新累積劑量
假定累積劑量是強度乘上時間(是這樣算沒有錯)
但是現在的時間應該怎摩表示?
> 請問這應該要怎麼寫
Delete我已經回答了。
> 但是現在的時間應該怎摩表示?
一般Arduino板子並無即時時鐘,沒辦法得知絕對時間,
但內部有個計時器,每當Reset時,就會從0開始起跳,
呼叫millis()的話,可得到該計時器的值(單位是千分之一秒),
因此兩次millis()呼叫的值,相減後,便可得到時間差。
我現在在做8顆燈泡
ReplyDelete每一顆燈泡都是點亮10秒然後關閉2秒
然後每顆燈泡都是和前面那一顆點亮時間相差1秒
請問這個要怎麼做
我都一直不成功
使用這篇介紹的計時器程式庫,應該很容易吧。
Delete你說的是需求,不是問題。
請問有什麼問題。
我不知道怎麼寫
Delete我寫的他都沒有辦法執行
大概是這樣吧
Deletehttp://pastebin.com/6ErnsCC8
不過裡頭只控制3顆LED。
其中的Flasher類別,修改自
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/a-classy-solution
好的 謝謝
Delete如果現在以壓力感測器測出的數值是A,過了五分鐘後的數值是B,然後如果數值沒有變,以LED亮紅燈,如果A<B,亮綠燈。程式要怎麼打呢? 我主要是想知道那個過了五分鐘後的時間程式怎麼寫?
ReplyDelete你需要記住A,以及測得A的時間(譬如呼叫millis()),
Delete然後不斷呼叫millis(),當新時間與測得A的時間相減後,超過5分鐘,
就去測量得到B。然後比較A與B,若<,就亮紅燈。
我有點不太懂欸 可不可以打一小段millis()的程式給我看看
Delete請參考
DeleteArduino練習:以開關切換LED是否閃爍
http://yehnan.blogspot.tw/2014/03/arduinoled.html
我有壓力計測出假如50kpa,今天我開始洩壓,洩至30kpa,我想要計時50至30之間的時間,這程式怎麼打?
ReplyDelete記住兩次測量的時間,然後相減。
Delete我回去試試,謝謝你
Delete葉大,
ReplyDelete我想請問一下
我將Timer.cpp, Timer.h, Event.cpp, Event.h都複製到專案底下了
Compile也沒問題
但是到了Linker那邊卻出現多重定義的錯誤
i.e.
sketch\lib/Event.cpp:34: multiple definition of `Event::Event()'
sketch\Event.cpp.o:sketch/Event.cpp:34: first defined here
sketch\lib\Event.cpp.o: In function `Event::Event()':
sketch\lib/Event.cpp:34: multiple definition of `Event::Event()'
... 之類的
不知道問題出在哪
謝謝!!
你的主程式檔.ino大概有#include 吧,
Delete改成"Timer.h"應該就好了。
自問自答一下好了
Delete在IDE當中
把那四有被自動加入的.h與.cpp在程式裡刪除後
重新再把檔案加入到專案裡就ok了
想不懂為什麼@@
錯誤訊息是multiple definition,代表重複定義,應該是專案納入兩支相同的檔案。
Delete> 將Timer.cpp, Timer.h, Event.cpp, Event.h都複製到專案底下了
為何如此?那是程式庫啊,應該放在程式庫目錄裡吧。
> 重新再把檔案加入到專案裡就ok了
不懂。
This comment has been removed by the author.
ReplyDelete請問我要做一個鬧鐘並在七段顯示器上呈現,當受定的時間到了就響鈴,然後按了一個按鈕讓響鈴停止並繼續時鐘模式,請問該怎麼做?
ReplyDelete拙作<>
Deletehttp://yehnan.blogspot.tw/2014/02/arduino_21.html
第5.4節,介紹四合一型的七段顯示器,以及即時時鐘(RTC)晶片,製作出一般的時鐘。但沒有鬧鐘功能。
鬧鐘功能需使用EEPROM來記錄使用者設定的響鈴時間。
至於你問該怎麼做,就做啊,呵呵。請不要問這種可以寫成一篇文章的問題。
那你可以告訴我停止時間的程式該怎麼寫嗎?
Delete停止響鈴?
Delete不就是停止輸出訊號給響鈴嗎?
可是我不是這樣寫的
Deleteif (mmm<0,sss<1) {
tone(Buzzer,440,1000);
}
要在響完之後加甚麼
if (mmm<0,sss<1) 這樣寫不對吧,應該用&&或||之類的。
Deletetone(Buzzer,440,1000);
不是有第三個參數1000嗎?應該會在1秒後停止。
是的,可是他變每隔一分鐘又在響一秒持續下去
ReplyDelete該怎麼讓它也停止時間呢?
if (mmm<0,sss<1) 的部份沒寫好。
Deleteif(mmm<0,sss<1){
ReplyDeletetone(Buzzer,440,1000);}
之後要加甚麼才能讓他停止繼續數
之前已經說了,if(mmm<0,sss<1)沒寫好。
Delete程式碼?
if(mmm<0&&sss<1){
ReplyDeletetone(Buzzer,440,1000);}
改了他到00:00的時候也不會叫了
什麼?
ReplyDelete給我全部的程式碼。
#include
ReplyDelete#include
#define Buzzer 6
#define DSP 1
// 鍵盤相關變數宣告
const byte ROWS = 3; // 鍵盤列數
const byte COLS = 3; // 鍵盤行數
char keys[ROWS][COLS] = { // 鍵盤對應按鍵值
{'0','1','2'},
{'3','4','5'},
{'6','7','8'} };
// 鍵盤的列與行接腳配置
byte rowPins[ROWS] = {A2,A1,A0};
byte colPins[COLS] = {A5,A4,A3};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
const unsigned char Pin_seg7[] = {13,12,11,10,9,8,7}; // 七段資料 abcdefg
const unsigned char Pin_scan[]={5,4,3,2}; // 七段掃描 千百十個
const unsigned char TAB[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x27,0x7f,0x67,0x00};
char dig[]={1,2,3,4}; // 七段顯示的數値()
char i,key,hh=10,mm=20,ss=10,mod=0;
char mmm=0,sss=11;
char mod1;
char m=12,x=33,z=30;
char mod2;
char mod3;
unsigned char ii=0,jj,s;
void ShowData(unsigned char dat);
// =============================================================
void setup() {
// 初始化:掃描式七段顯示器
for(i=0;i<7;i++) pinMode(Pin_seg7[i],OUTPUT);
for(i=0;i<4;i++) {pinMode(Pin_scan[i],OUTPUT);digitalWrite(Pin_scan[i],LOW);}
// 初始化:矩陣式鍵盤初始化
keypad.addEventListener(keypadEvent); // Add an event listener for this keypad
pinMode(Buzzer, OUTPUT); pinMode(DSP, OUTPUT);
digitalWrite(DSP,LOW);
Timer1.initialize(1000000);
Timer1.attachInterrupt( timerIsr ); // attach the service routine here
}
// =============================================================
void loop() {
ShowData(TAB[dig[ii]]);
// 指定掃描位置
digitalWrite(Pin_scan[ii],HIGH); delay(5); digitalWrite(Pin_scan[ii],LOW);
if(++ii>=4) ii=0;
key = keypad.getKey();
if (key) {tone(Buzzer,440,100);
switch(key) {
case '0': if(++mod>=2) mod=0; break;
case'1':if(++mod1>=2) mod1=0 ;break;
case'3':
hh++;if(hh>24) hh=0;break;
case'4':
mm++;if(mm>=60) mm=0;break;
case'6':
mmm++;if(mmm>=60) mmm=0;break;
case'7':
sss++;if(sss>=60) sss=0;
}
}
}
// =============================================================
void ShowData(unsigned char dat)
{
s=0x01;
for(jj=0;jj<7;jj++) {
if(dat & s) digitalWrite(Pin_seg7[jj],HIGH);
else digitalWrite(Pin_seg7[jj],LOW);
s<<=1;
}
}
// =============================================================
void keypadEvent(KeypadEvent key){
switch (keypad.getState()){
case PRESSED:
if (key == '1') {
}
break;
case RELEASED:
if (key == '2') {
}
break;
case HOLD:
if (key == '3') {
}
break;
}
}
// =============================================================
void timerIsr()
{ digitalWrite( DSP, digitalRead( DSP ) ^ 1 );
{if(++ss>=60) {ss=0;
if(++mm>=60) {mm=0;
if(++hh>=24) hh=0;
}
}
if (hh<11,mm<1) {
tone(Buzzer,440,1000);
}
switch(mod) {
case 0:
dig[0]=mm/10;dig[1]=mm%10;dig[2]=ss/10;dig[3]=ss%10;
break;
case 1:
dig[0]=hh/10;dig[1]=hh%10;dig[2]=mm/10;dig[3]=mm%10;
break;}}
{if(--sss<0) {sss=59;
if(--mmm<0) mmm=59;
}
if (mmm<1,sss<1) {
tone(Buzzer,440,1000);
}
switch(mod1){
case 0:
dig[0]=mmm/10;dig[1]=mmm%10;dig[2]=sss/10;dig[3]=sss%10;
break;}
}
}
我要問要怎麼設定倒數時間跟鬧鐘時間
ReplyDelete以及時間到了它不再繼續數下去
#include
ReplyDelete#include
#define Buzzer 6
#define DSP 1
// 鍵盤相關變數宣告
const byte ROWS = 3; // 鍵盤列數
const byte COLS = 3; // 鍵盤行數
char keys[ROWS][COLS] = { // 鍵盤對應按鍵值
{'0','1','2'},
{'3','4','5'},
{'6','7','8'} };
// 鍵盤的列與行接腳配置
byte rowPins[ROWS] = {A2,A1,A0};
byte colPins[COLS] = {A5,A4,A3};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
const unsigned char Pin_seg7[] = {13,12,11,10,9,8,7}; // 七段資料 abcdefg
const unsigned char Pin_scan[]={5,4,3,2}; // 七段掃描 千百十個
const unsigned char TAB[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x27,0x7f,0x67,0x00};
char dig[]={1,2,3,4}; // 七段顯示的數値()
char i,key,hh=10,mm=20,ss=10,mod=0;
char mmm=0,sss=11;
char mod1;
char m=12,x=33,z=30;
char mod2;
char mod3;
unsigned char ii=0,jj,s;
void ShowData(unsigned char dat);
// =============================================================
void setup() {
// 初始化:掃描式七段顯示器
for(i=0;i<7;i++) pinMode(Pin_seg7[i],OUTPUT);
for(i=0;i<4;i++) {pinMode(Pin_scan[i],OUTPUT);digitalWrite(Pin_scan[i],LOW);}
// 初始化:矩陣式鍵盤初始化
keypad.addEventListener(keypadEvent); // Add an event listener for this keypad
pinMode(Buzzer, OUTPUT); pinMode(DSP, OUTPUT);
digitalWrite(DSP,LOW);
Timer1.initialize(1000000);
Timer1.attachInterrupt( timerIsr ); // attach the service routine here
}
// =============================================================
void loop() {
ShowData(TAB[dig[ii]]);
// 指定掃描位置
digitalWrite(Pin_scan[ii],HIGH); delay(5); digitalWrite(Pin_scan[ii],LOW);
if(++ii>=4) ii=0;
key = keypad.getKey();
if (key) {tone(Buzzer,440,100);
switch(key) {
case '0': if(++mod>=2) mod=0; break;
case'1':if(++mod1>=2) mod1=0 ;break;
case'3':
hh++;if(hh>24) hh=0;break;
case'4':
mm++;if(mm>=60) mm=0;break;
case'6':
mmm++;if(mmm>=60) mmm=0;break;
case'7':
sss++;if(sss>=60) sss=0;
}
}
}
// =============================================================
void ShowData(unsigned char dat)
{
s=0x01;
for(jj=0;jj<7;jj++) {
if(dat & s) digitalWrite(Pin_seg7[jj],HIGH);
else digitalWrite(Pin_seg7[jj],LOW);
s<<=1;
}
}
// =============================================================
void keypadEvent(KeypadEvent key){
switch (keypad.getState()){
case PRESSED:
if (key == '1') {
}
break;
case RELEASED:
if (key == '2') {
}
break;
case HOLD:
if (key == '3') {
}
break;
}
}
// =============================================================
void timerIsr()
{ digitalWrite( DSP, digitalRead( DSP ) ^ 1 );
{if(++ss>=60) {ss=0;
if(++mm>=60) {mm=0;
if(++hh>=24) hh=0;
}
}
if (hh<11,mm<1) {
tone(Buzzer,440,1000);
}
switch(mod) {
case 0:
dig[0]=mm/10;dig[1]=mm%10;dig[2]=ss/10;dig[3]=ss%10;
break;
case 1:
dig[0]=hh/10;dig[1]=hh%10;dig[2]=mm/10;dig[3]=mm%10;
break;}}
{if(--sss<0) {sss=59;
if(--mmm<0) mmm=59;
}
if (mmm<1,sss<1) {
tone(Buzzer,440,1000);
}
switch(mod1){
case 0:
dig[0]=mmm/10;dig[1]=mmm%10;dig[2]=sss/10;dig[3]=sss%10;
break;}
}
}
mod1若為0應該是代表倒數模式吧,而mmm和sss代表要倒數的分鐘與秒數。
Delete如果我說的沒錯,那麼應該把
if(--sss<0){
sss=59;
if(--mmm<0)
mmm=59;
}
if(mmm<1,sss<1){
tone(Buzzer,440,1000);
}
搬進switch(mod1)的case 0裡。
而且,if(mmm<1,sss<1)應該改成if(mmm<1 && sss<1),也就是當倒數到0分0秒時,發出叫聲。
然後應該把mod1設為1,解除倒數模式。
對你說的沒錯,mmm和sss是倒數的分秒
ReplyDeletemod1設為1該寫在哪?
ReplyDelete我現在還有一個問題,一開始燒錄進去就開始倒數,請問要一開始是時鐘,按下一個按鈕才變倒數該怎麼做?
ReplyDelete> mod1設為1該寫在哪?
ReplyDelete需要解除倒數模式的地方。
> 要一開始是時鐘,按下一個按鈕才變倒數該怎麼做?
改程式。
你說的是需求,不是問題。
程式裡有switch(mod)和switch(mod1)的部份,混雜了一般時鐘模式和倒數模式,應從該處下手開始改。
要回答的話,非三言兩語辦得到。
加油。
case '0': if(++mod>=2) mod=0; break;
ReplyDeletecase'1':if(++mod1>=2) mod1=0 ;break;
這段該怎麼改,才不會混淆
這段會混淆嗎?
Deleteint setringcount=0;
ReplyDeletevoid setRing()
{
switch(setringcount)
{
case 0:
h += key*10;
break;
case 1:
h+=key;
break;
case 2:
m+=key*10;
break;
case 3:
m+=key;
break;
}
if(setringcount<3)
setringcount++;
else
{
setringcount=0;
setring=false;
}
鬧鐘這樣寫有錯嗎?
你這樣問,不知如何回答。
Delete您好!
ReplyDelete我用這個TIMER加上開關控制LED燈在固定的時間內閃爍,時間到自己關掉,程式如下
#include "Timer.h"
#include
Bounce bouncer = Bounce(2, 50);
static int ledStatus = LOW;
Timer t;
int ledEvent;
int ledEvent2;
void setup()
{
Serial.begin(9600);
pinMode(2,INPUT);
pinMode(13, OUTPUT);
pinMode(3, OUTPUT);
ledEvent = t.oscillate(13, 20, ledStatus);
ledEvent2 = t.oscillate(3, 1000, ledStatus);
int afterEvent = t.after(9000, doAfter);
}
void loop()
{
if(bouncer.update() == true && bouncer.read() == HIGH) {
t.update();
}
}
void doAfter() ///after的callback
{
Serial.println("stop the led event");
t.stop(ledEvent2);
t.stop(ledEvent);
}
但是執行後按下開關會造成燈泡恆亮,不會閃爍也不會自行關掉,請問是哪邊出問題了呢
加了開關的程式才會這樣,只用TIMER可以定時關掉。
嗯,t.update()要不斷呼叫,timer才能運作。
Delete大概是這樣吧,沒測試過,請試試看。
http://pastebin.com/4esFRjn6
#include "Timer.h"
#include
Bounce bouncer = Bounce(2, 50);
static int ledStatus = LOW;
Timer t;
int ledEvent;
int ledEvent2;
void setup()
{
Serial.begin(9600);
pinMode(2,INPUT);
pinMode(13, OUTPUT);
pinMode(3, OUTPUT);
ledEvent = t.oscillate(13, 20, ledStatus);
ledEvent2 = t.oscillate(3, 1000, ledStatus);
}
void loop()
{
t.update();
if(bouncer.update() == true && bouncer.read() == HIGH) {
int afterEvent = t.after(9000, doAfter);
}
}
void doAfter() ///after的callback
{
Serial.println("stop the led event");
t.stop(ledEvent2);
t.stop(ledEvent);
}
您好,經過改良有達到我的需求囉!!!謝謝您!
Delete老師想請問一下
ReplyDelete如果使用stop後有辦法再重新update嗎?
不行。
Delete重新呼叫every或oscillate或after或pulse。
我現在用兩塊板子
Delete我使用every讓第一塊板子丟訊號給第二塊使第二塊板子作動
但是我希望第二塊作動完後丟訊號回第一塊使第一塊板子停止丟訊號
但是我兩塊板子是反覆作動的,所以我想說可否使用stop去達成
下達stop,之前的every設定的東西就不見了。
Delete再重新下達every囉。
不好意思 如果stop的是every那我stop的括號要寫甚麼呢?
Delete像是這樣printYtimer.stop();
因為範例是要再()裡放Event
可是我沒有Event可以放耶
呼叫stop時,要傳入event的id,
Delete要不然它怎麼知道該停止哪個event。
呼叫every時,會回傳event的id。
您好,
ReplyDelete請問我想讓伺服馬達每1秒轉10度用timer該怎麼做呢?
嗯,譬如每隔0.1秒下達一次控制指令,每次往前加1度;
Delete直到(10次)1秒。
自己控制滿麻煩的吧,找找有無現成的程式庫。
葉老師~
Delete為什麼我按照您的方法匯入程式庫但是一直失敗,有丟到libraries資料夾了
但是還是一直編譯錯誤,跟軟體的版本有關係嗎? 謝謝!
錯誤訊息是什麼。
ReplyDeleteTimer程式庫有新舊版。
Arduino:1.6.10 (Windows 7), 板子:"Arduino/Genuino Uno"
ReplyDeleteIn file included from C:\Users\ives\Documents\Arduino\bending\_20160811-timer\_20160811-timer.ino:5:0:
C:\Users\ives\Documents\Arduino\libraries\ServoTimer1/ServoTimer1.h:25:20: fatal error: wiring.h: No such file or directory
#include
^
compilation terminated.
exit status 1
板子Arduino/Genuino Uno編譯錯誤
This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
再麻煩您幫我看看~謝謝囉
ReplyDelete你用的程式庫ServoTimer1,需要wiring.h,但找不到。
ReplyDeleteOK~謝謝您了!
ReplyDelete我想問一下有沒有60秒倒數像投籃機一樣的程式
ReplyDeleteThis comment has been removed by the author.
ReplyDelete我想問一下有沒有60秒倒數像投籃機一樣的程式
ReplyDelete寫出來就有了。
Delete有何問題?
請問如何知道通過用串口發送數據時,兩個數據接收的時間差?
ReplyDelete要由接收方告知傳送方吧。
Delete葉難大大,不好意思,請問。
ReplyDeletevoid setup() {
Serial.begin(9600);
}
void loop(){
Serial.println("1");
Serial.println("2");
delay(1000);
我能不能夠一開始只print "1"
之後讓程式重頭開始然後只print"2" 呢?
有什麼方式或是指令能夠讓loop分開執行嗎?
開機後只需執行一次的,放在setup裡。
Delete什麼叫做"讓程式重頭開始"?
葉大您好~我想請問如果現在我要將兩組電路訊號輸入到ARDUINO裡面
ReplyDelete想經由ARDUINO判斷兩個訊號到達的峰值的時間差大小有什麼資料可以參考的嗎??
時間差介於90~130us之間(擷取頻率大約500KHZ),不知ARDUINO是否能夠偵測到這麼短的時間差?
根據analogRead的文件
Deletehttps://www.arduino.cc/en/Reference/AnalogRead
the maximum reading rate is about 10,000 times a second.
也就是10KHZ,
沒法滿足你的需求。
請問葉大如果想在程式內加插一些功能:
ReplyDelete原本的程式是升到某個溫度就會開風扇,降到某個溫度就會關閉,
如果想開了那個風扇後,過了5分鐘,它降不了那個溫度自動就會關閉,
應該怎樣做??
> 自動就會關閉,
Delete關閉什麼?
用有限狀態機的程式寫法。
葉大想請教你
ReplyDelete如果我的A程式控制 ,但需要每隔1小時叫出B程式,並由B程式優先控制一段時間,再回A程式控制,這也可以用計時器寫嗎?我有查到有人說可以用watch dog 但不知道怎麼下手
void func_a(){}
Deletevoid func_b(){}
int state;
void loop(){
if(state){
func_a();
}
else{
func_b();
}
}
或許可以這麼寫,裡面要加入計時器與程式邏輯,控制state的值,切換要執行的函式。
至於watchdog,那是另一回事。
Delete請問若我想利用程式庫在序列埠每0.5秒的速度顯示出數字(50/100),其中數字需要讓他以每2秒的速度做切換,該怎麼做比較好呢?
ReplyDelete希望能達到功能如下:
50
50
50
50
100
100
100
100
50
50
50
50
這些數字出現能在0.5秒後出現
大概是這樣吧,沒真正執行過
Deletehttps://pastebin.com/hHe5CJcP
#include
Timer timer;
int count = 0;
int n = 50;
void callbackfunction(){
Serial.println(n);
count++;
if(count == 4){
count = 0;
n = n == 50 ? 100 : 50;
}
}
void setup() {
Serial.begin(115200);
timer.every(500, callbackfunction);
}
void loop() {
timer.update();
}
我用的Timer程式庫1.x版,不是2.x版。
請問我如果要設定燈亮5秒停止,我用pulse的話5秒停了,但之後又亮,有指令可以解決嗎?謝謝
ReplyDelete改用after
Delete謝謝 那如果我是要讓燈5秒自動停,可以怎麼寫,謝謝
Delete大概是這樣
Deletet.after(5000, doAfter);
t是Timer,5000毫秒,時間到了會呼叫doAfter,你就可以doAfter裡熄燈
HELLO 葉大
ReplyDelete拜讀了您的網誌
已經對Timer的使用 有很多的了解
但是目前有個疑問
Timer的功能
只能照固定的時間點滅
比如上面的例子是1秒亮1秒滅
那如果我要改成
2秒亮 10秒滅
在不使用delay的狀態下
這還是用Timer嗎?
還是要用其他的寫法
謝謝
用兩個timer,都是只觸發一次,
Delete首先點亮LED,
第一個timer設2秒,時間到了熄滅LED,並且啟動第二個timer。
第二個timer設10秒,時間到了點亮LED,並且啟動第一個timer。
葉大您好~請問~
ReplyDelete我用了attachInterrupt與Timer一起使用,
發現有時後觸發中斷時會造成當機,
是因為Timer在運行時不能觸發attachInterrupt嗎
可以。
Delete程式碼?
可以喔~星期一寄您信箱~不過超長~用來控制5個I2C顯示器的~
Delete貼上https://pastebin.com/
Deletehttps://pastebin.com/VE2yZAGi
Delete請葉大看看~在166行
嗯,果然很長,慢慢看。
Delete有個小問題,
全域變數,如果會被中斷回呼函式(如ert0)與一般函式(如ce)存取,
宣告時應該加上volatile。
參考
https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
對了~板子是Mega2560
Delete我看了程式碼,但沒有實際硬體可執行,只能憑空推測。
Delete我看不出Timer程式庫的部份有何問題,
這程式庫是利用millis取得的時間,時間到了就執行你指定的回呼函式,屬於軟體計時器。
更新了程式
Deletehttps://pastebin.com/fBvPZa61
把Timer改成用73~74與183~185行搭配~
再測試後發現「實際值+1」的過程當下,如果按很快時,時間到要存檔才會發生當機,
有沒有可能改成,時間到,實際值又完成+1後,才執行存檔,
或跑「實際值+1」的過程中跳過存檔?
相關行數
43 <-按鈕去抖
50~51 <-按鈕去抖全域宣告
73~74 <-存檔時間計算用
183~185 <-存檔時間到存檔
285~291 <-中斷引腳
我改用
DeleteWDT:Watch Dog Timer
如果當機超過2S就重新開機
不過變成每次實際+1都要執行EEPROM
抱歉,沒幫上忙。
Delete想請問葉大
ReplyDelete我想在我的程式中加入delay
可是delay 會影響整個程式的運作
請問要如何取代delay 的功能, 而不讓程式有問題呢
例如 :
int pin = 10;
int photocellPin1 = 3;
int val2 = 0;
void setup() {
pinMode(pin, OUTPUT);
}
void loop() {
val2 = digitalRead(photocellPin1);
if(val2==HIGH){
delay(5000);
digitalWrite(pin, HIGH);
}
else{
digitalWrite(pin, LOW);
}
}
當 val2 成立時, 會delay 5秒後 pin HIGH
但是當 val2 不成立時,也會延遲幾秒才變成pin LOW
請問要如何去修改這個延遲的功能呢
感謝
怎麼會? 根據程式碼,val2為LOW時,馬上就會pin LOW啊。
Delete根據程式碼的運作, val2為LOW時,應該會馬上就 pin LOW...但實際測試 , 卻會延遲幾秒才會動作, 我也很困惱為何會這樣
Delete另外最大問題, 有了delay 這個指令後,整體其他的判斷式也會受到delay 這個指令的影響而也會有些許延遲的狀況...
所以想是否有其他的取代方式
謝謝大大
行為要再說清楚一點。
Deleteval2==HIGH時,你需要等5秒後,才pin HIGH,
那麼在這5秒之中,若val2的值又變了呢?
val2==LOW時,立即pin LOW嗎?
謝謝大大
Delete是的..在delay的這5秒間, 如果val2 又變回LOW , 那pin 還是要變回LOW..
而delay 的部分要重新計算...以val2 變回HIGH 回時重新算5 秒..
作法之一如下
Deleteint pin = 10;
int photocellPin1 = 3;
int val2 = 0;
#define DELAY 5000
int flag = LOW;
unsigned long t_old;
void setup() {
pinMode(pin, OUTPUT);
pinMode(photocellPin1, INPUT);
}
void loop() {
val2 = digitalRead(photocellPin1);
if(val2 == HIGH){
if(flag == LOW){
t_old = millis();
flag = HIGH;
}
}
else{
digitalWrite(pin, LOW);
}
if(flag == HIGH && (millis() - t_old >= DELAY)){
digitalWrite(pin, HIGH);
flag = LOW;
}
}
試試看吧。
感謝葉大幫忙
ReplyDelete測試起來可以替代delay 的功能
但是有發現一個問題
當 val2 變 HIGH 成立時, 會delay 5秒後 pin HIGH
但是 val2 變 LOW 時. 如果間隔沒有到達 5 秒, val2 又變 HIGH.
此時不會delay 5 秒才 pin HIGH. 會大約1~3 秒就 pin HIGH (不定時間)
請問這是正常的嗎?要如何可以確保一定要5秒後才可以pin HIGH
另外是否可以使用LCD 螢幕去顯示秒數時間 (使用I2C 的方式)
感謝大大幫忙
> 但是有發現一個問題...
Delete嗯,我程式沒寫好,漏了這點。
請你自己改改看吧。
> 另外是否可以使用LCD 螢幕去顯示秒數時間
可以。
感謝葉大
Delete葉大幫我寫的已經有點超出我會的..(持續會學習中...)
不過自己怎麼改..越改越糟..哈哈哈..能力不足的新手
不知大大可否再幫忙修改一下
感恩大大教導....
把
Deleteelse{
digitalWrite(pin, LOW);
}
改成
else{
digitalWrite(pin, LOW);
flag = LOW;
}
可能就可以了。
> 不知大大可否再幫忙修改一下
不行。
感謝葉大
Delete原來這麼簡單就可以修正問題
我會持續去學習修改
感恩~
葉大你好,目前寫了兩個LED時間延遲,但是想要能夠互不干涉的情況下啟動,例如sw1拉HIGH後LED1打開延遲5秒關閉,這個區間sw2拉HIGH也能夠啟動LED2打開延遲10秒關閉,因為用DELAY好像沒辦法做到,想請問我該如何作修改呢??
ReplyDeleteconst byte LED1 = 9;
const byte LED2 = 10;
const byte sw1 = 5;
const byte sw2 = 6;
void setup()
{
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(sw1, INPUT);
pinMode(sw2, INPUT);
}
void loop()
{
boolean val = digitalRead(sw1);
boolean va2 = digitalRead(sw2);
if(val==HIGH){
digitalWrite(LED1,HIGH);
delay(5000);
digitalWrite(LED1,LOW);
}
if(va2==HIGH){
digitalWrite(LED2,HIGH);
delay(10000);
digitalWrite(LED2,LOW);
}
}
如果我想要"光敏電阻遮住時蜂鳴器叫一聲"
ReplyDelete能怎麼寫 求大神指教