過去我們在偵測button有沒有按下時,我們會在loop() 中不斷的去檢查button狀態,
但這樣其實效能不佳,我們可以透過中斷,來處理Button按下的問題。
但記住一件事,傳統的button會有彈跳現像,這個我們之後再談。
2018年9月14日 星期五
2017年10月9日 星期一
[arduino] Arduino Nano 使用DFRobot LCD KeyPad Shield
首先介紹一下二個主角
- Arduino Nano
- 官方的說明 -->The Arduino Nano is a compact board similar to the UNO. (Arduino Nano是一塊類似於UNO的簡潔版)
它的體型比較小,而且Analog Input有8支,比UNO多二支。
資料及圖片來源:
https://store.arduino.cc/usa/arduino-nano - DFRobot LCD KeyPad Shield
- 這是一塊For UNO的擴充版,上面有一個1602LCD及幾個按鈕。
- 使用到的PIN如下表
Analog 0是透過不同按鈕會輸出不同的電壓來分辨是那一個按鈕if (adc_key_in < 50) return btnRIGHT; if (adc_key_in < 250) return btnUP; if (adc_key_in < 450) return btnDOWN; if (adc_key_in < 650) return btnLEFT; if (adc_key_in < 850) return btnSELECT;
Analog 0 Button (select, up, right, down and left) Digital 4 DB4 Digital 5 DB5 Digital 6 DB6 Digital 7 DB7 Digital 8 RS (Data or Signal Display Selection) Digital 9 Enable Digital 10 Backlit Control
我們依照以上接腳定義,將其接上nano的D4~D10即可使用1602LCD的功能。
2017年9月30日 星期六
[Arduino]MCP4728 修改I2C Address
MCP4728簡介如下:
Features
• 12-Bit Voltage Output DAC with Four Buffered Outputs
• On-Board Nonvolatile Memory (EEPROM) for DAC Codes and I2C™ Address Bits
• Internal or External Voltage Reference Selection
• Output Voltage Range:
- Using Internal VREF (2.048V):
0.000V to 2.048V with Gain Setting = 1
0.000V to 4.096V with Gain Setting = 2
- Using External VREF (VDD):
0.000V to VDD • ±0.2 Least Significant Bit (LSB) Differential Nonlinearity (DNL) (typical)
• Fast Settling Time: 6 µs (typical)
• Normal or Power-Down Mode
• Low Power Consumption
• Single-Supply Operation: 2.7V to 5.5V
• I2C Interface: - Address bits: User Programmable to EEPROM
- Standard (100 kbps), Fast (400 kbps) and High Speed (HS) Mode (3.4 Mbps) • 10-Lead MSOP Package • Extended Temperature Range: -40°C to +125°C
資料來源:
http://ww1.microchip.com/downloads/en/DeviceDoc/22187E.pdf
它是一顆擁有四個類比輸出的DAC,透過I2C介面做控制。
12Bit 解析度,可以把輸出的電壓分成4096等份,例如輸出0~5V, 則可以將電壓的精準度
達到 0.001。
可有以下幾種模式(透過指令即可變更):
使用外部參考電壓,例如5V 。
使用內部參考電壓 Gain =1
0.000V to 2.048V
使用內部參考電壓 Gain =2
0.000V to 4.096V
如果要求精準,建議使用內部參考電壓 Gain =1 ,並透過穩壓IC供電給DAC。
如果要使用多組DAC在電路中,則需要改變MCP4728的I2C位置,我們先來看一下
datasheet中的說明
把現有的位置和新的位置依照上面填空後,就是4個byte的指令,接下來要接上
LDAC這支Pin腳,它需要在第2個byte傳送到第8個bit時,將LDAC由HIGH轉LOW,
否則怎麼傳送指令都會失敗,注意必須在MCP4728回傳ACK 之前就要轉LOW,
所以用Arduino內建的Wire.Write會失敗,弄了2天,請教了Firmware朋友,得到的
答案建議自己去控制I2C通訊時的HIGH ,LOW ,才能在第八個bit時將LDAC轉LOW,
等到ACK回來就來不了。來看一下接線
Arduino UNO MCP4728
VCC VDD
GND VSS
2 SCL
3 SDA
8 LDAC
接下來是我去網路上找的一份程式碼,整理之後,確認可以成功。
將預設的0x61位置改成0x60
執行完畢後,請記得將Arduino 斷電,重新上電後用I2C Scanner 範例程式來確認是否有改成功。
參考來源:
https://forum.pjrc.com/threads/28424-Programming-the-I2C-address-of-a-MCP4728-4-channel-DAC-using-i2c_t3
https://github.com/TrippyLighting/SoftI2cMaster/blob/master/SoftI2cMaster.cpp
Features
• 12-Bit Voltage Output DAC with Four Buffered Outputs
• On-Board Nonvolatile Memory (EEPROM) for DAC Codes and I2C™ Address Bits
• Internal or External Voltage Reference Selection
• Output Voltage Range:
- Using Internal VREF (2.048V):
0.000V to 2.048V with Gain Setting = 1
0.000V to 4.096V with Gain Setting = 2
- Using External VREF (VDD):
0.000V to VDD • ±0.2 Least Significant Bit (LSB) Differential Nonlinearity (DNL) (typical)
• Fast Settling Time: 6 µs (typical)
• Normal or Power-Down Mode
• Low Power Consumption
• Single-Supply Operation: 2.7V to 5.5V
• I2C Interface: - Address bits: User Programmable to EEPROM
- Standard (100 kbps), Fast (400 kbps) and High Speed (HS) Mode (3.4 Mbps) • 10-Lead MSOP Package • Extended Temperature Range: -40°C to +125°C
資料來源:
http://ww1.microchip.com/downloads/en/DeviceDoc/22187E.pdf
它是一顆擁有四個類比輸出的DAC,透過I2C介面做控制。
12Bit 解析度,可以把輸出的電壓分成4096等份,例如輸出0~5V, 則可以將電壓的精準度
達到 0.001。
可有以下幾種模式(透過指令即可變更):
使用外部參考電壓,例如5V 。
使用內部參考電壓 Gain =1
0.000V to 2.048V
使用內部參考電壓 Gain =2
0.000V to 4.096V
如果要求精準,建議使用內部參考電壓 Gain =1 ,並透過穩壓IC供電給DAC。
如果要使用多組DAC在電路中,則需要改變MCP4728的I2C位置,我們先來看一下
datasheet中的說明
MCP4728預設的I2C位置為0x61 , 也就是1100001 ,也就是A2=0 , A1=0 ,A0=1 。
如果今天我要把它改成0x60 (1100000), New Address bit A2=0, A1=0 , A0=0 。
LDAC這支Pin腳,它需要在第2個byte傳送到第8個bit時,將LDAC由HIGH轉LOW,
否則怎麼傳送指令都會失敗,注意必須在MCP4728回傳ACK 之前就要轉LOW,
所以用Arduino內建的Wire.Write會失敗,弄了2天,請教了Firmware朋友,得到的
答案建議自己去控制I2C通訊時的HIGH ,LOW ,才能在第八個bit時將LDAC轉LOW,
等到ACK回來就來不了。來看一下接線
Arduino UNO MCP4728
VCC VDD
GND VSS
2 SCL
3 SDA
8 LDAC
接下來是我去網路上找的一份程式碼,整理之後,確認可以成功。
將預設的0x61位置改成0x60
// R/W direction bit to OR with address for start or restart #define I2C_READ 1 #define I2C_WRITE 0 #define SOFT_I2C_MASTER #define I2C_DELAY_USEC 10 #define ldac_pin 8 uint8_t sclPin_; uint8_t sdaPin_; unsigned char OldAddr = 0x1; //0x61 default I2C Address unsigned char NewAddr = 0x0; //0x60 new I2C Address //I2C Init void init(uint8_t sclPin, uint8_t sdaPin) { sclPin_ = sclPin; sdaPin_ = sdaPin; pinMode(sclPin_, OUTPUT); digitalWrite(sdaPin_, HIGH); //Mark_H fix pinMode(sdaPin_, OUTPUT); digitalWrite(sclPin_, HIGH); digitalWrite(sdaPin_, HIGH); } //------------------------------------------------------------------------------ // read a byte and send Ack if last is false else Nak to terminate read uint8_t read(uint8_t last) { uint8_t b = 0; // make sure pullup enabled digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, INPUT); // read byte for (uint8_t i = 0; i < 8; i++) { // don't change this loop unless you verify the change with a scope b <<= 1; delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, HIGH); if (digitalRead(sdaPin_)) b |= 1; digitalWrite(sclPin_, LOW); } // send Ack or Nak digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, OUTPUT); digitalWrite(sdaPin_, last); digitalWrite(sclPin_, HIGH); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, LOW); digitalWrite(sdaPin_, HIGH); return b; } //------------------------------------------------------------------------------ // send new address and read/write without stop uint8_t restart(uint8_t addressRW) { digitalWrite(sclPin_, HIGH); return start(addressRW); } //------------------------------------------------------------------------------ // issue a start condition for i2c address with read/write bit uint8_t start(uint8_t addressRW) { digitalWrite(sdaPin_, LOW); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, LOW); return write(addressRW); } //------------------------------------------------------------------------------ // issue a stop condition void stop(void) { digitalWrite(sdaPin_, LOW); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, HIGH); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sdaPin_, HIGH); delayMicroseconds(I2C_DELAY_USEC); } //------------------------------------------------------------------------------ // write byte and return true for Ack or false for Nak uint8_t write(uint8_t b) { // write byte for (uint8_t m = 0X80; m != 0; m >>= 1) { digitalWrite(sdaPin_, m & b); digitalWrite(sclPin_, HIGH); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, LOW); } // get Ack or Nak digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, INPUT); digitalWrite(sclPin_, HIGH); b = digitalRead(sdaPin_); digitalWrite(sclPin_, LOW); digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, OUTPUT); return b == 0; } //------------------------------------------------------------------------------ // write byte and return true for Ack or false for Nak uint8_t ldacwrite(uint8_t b, uint8_t ldacpin) { // write byte for (uint8_t m = 0X80; m != 0; m >>= 1) { // don't change this loop unless you verivy the change with a scope digitalWrite(sdaPin_, m & b); digitalWrite(sclPin_, HIGH); delayMicroseconds(I2C_DELAY_USEC); digitalWrite(sclPin_, LOW); } // get Ack or Nak digitalWrite(ldacpin, LOW); digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, INPUT); digitalWrite(sclPin_, HIGH); // digitalWrite(bsy_pin,LOW); b = digitalRead(sdaPin_); digitalWrite(sclPin_, LOW); digitalWrite(sdaPin_, HIGH); pinMode(sdaPin_, OUTPUT); return b == 0; } void setup() { Serial.begin(9600); pinMode(ldac_pin,OUTPUT); digitalWrite(ldac_pin,HIGH); delay(10); init (2,3); uint8_t data1=(unsigned char)(0B11000000 | (OldAddr << 1)); uint8_t data2=(unsigned char)(0B01100001 | (OldAddr << 2)); uint8_t data3=(unsigned char)(0B01100010 | (NewAddr << 2)); uint8_t data4=(unsigned char)(0B01100011 | (NewAddr << 2)); uint8_t ack1=start(data1); uint8_t ack2=ldacwrite(data2,ldac_pin); uint8_t ack3=write(data3); uint8_t ack4=write( data4); stop(); Serial.println(data1); Serial.println(data2); Serial.println(data3); Serial.println(data4); Serial.println(ack1); Serial.println(ack2); Serial.println(ack3); Serial.println(ack4); delay(10); Serial.println("FINISH"); } void loop() { // put your main code here, to run repeatedly: }
參考來源:
https://forum.pjrc.com/threads/28424-Programming-the-I2C-address-of-a-MCP4728-4-channel-DAC-using-i2c_t3
https://github.com/TrippyLighting/SoftI2cMaster/blob/master/SoftI2cMaster.cpp
2017年9月16日 星期六
「LinkIt Samrt 7688][Arduino] 7688與Arduino Nano USB通訊
最近有一個需求,讓7688與Arduino透過USB通訊,想到的解法是Arduino本身的USB
預設為USB to Serial,只要能讓7688上的OpenWRT支援USB to Serial晶片驅動即可。
以下做了個簡單的Demo,附上Arduino及7688上python code做個Echo 測試。

這次用的是Arduino Nano,它使用的USB to Serial是 CH340 (我這塊不是原版的)
首先我需要安裝usbutils , 讓我可以使用lsusb來詢連接的USB裝置。(可以不裝)
opkg install usbutils

接下來安裝CH340驅動程式
預設為USB to Serial,只要能讓7688上的OpenWRT支援USB to Serial晶片驅動即可。
以下做了個簡單的Demo,附上Arduino及7688上python code做個Echo 測試。
這次用的是Arduino Nano,它使用的USB to Serial是 CH340 (我這塊不是原版的)
首先我需要安裝usbutils , 讓我可以使用lsusb來詢連接的USB裝置。(可以不裝)
opkg install usbutils
接下來安裝CH340驅動程式
opkg install kmod-usb-serial-ch341
Arduino程式碼
一開始連通Serial Port時會輸出hello form Arduino
之後會回應所收到的值,做一個Echo
接下來是7688上的python程式碼
CH340裝置的名稱會是 /dev/ttyUSBx ,像我是/dev/ttyUSB0 ,換成自己裝置的名字。
一連Serial Port會傳送Hello from python ,之後會每隔一秒傳送目前時間,並印出收到的訊息。
執行結果
2017年8月1日 星期二
2017年7月31日 星期一
「Arduino」Digital HIGH /LOW 定義
HIGH: a voltage greater than 3.0V is present at the pin (5V boards);
Low: voltage less than 1.5V is present at the pin (5V boards);
2017年6月8日 星期四
[Arduino]NTSC 4K7 量測溫度方法.
最近遇到一個溫度計,它只有2條線,Orz ,看起來不可能是1-Wire,
研究了一下發現是電阻式的溫度計,也就是熱敏電阻。
在25度西時大約是4.77K歐姆,在30度西時是38.8K歐姆,代表溫度上升阻值下降,
所以不可能是有名的PT100(0度西時阻值為100歐姆),去查了一下類似的數據,

接下來是該如何得到阻值變化 ----> 串聯一個固定阻值的電阻,就可以依電壓
變化的比例,知道目前溫度計的阻值,上面的4.7K表示溫度計的阻值,
下面量測的部份則是固定阻值的4.7K電阻。
研究了一下發現是電阻式的溫度計,也就是熱敏電阻。
在25度西時大約是4.77K歐姆,在30度西時是38.8K歐姆,代表溫度上升阻值下降,
所以不可能是有名的PT100(0度西時阻值為100歐姆),去查了一下類似的數據,
接下來是該如何得到阻值變化 ----> 串聯一個固定阻值的電阻,就可以依電壓
變化的比例,知道目前溫度計的阻值,上面的4.7K表示溫度計的阻值,
下面量測的部份則是固定阻值的4.7K電阻。
2017年6月7日 星期三
[Arduino] I2C Scann
最近使用DAC時,看了很多文件和範例,都沒有反應,原因是I2C Address錯了,
當你使用I2C時,其實有一個方法可以掃描出正確的I2C Address
資料來源:
https://playground.arduino.cc/Main/I2cScanner
當你使用I2C時,其實有一個方法可以掃描出正確的I2C Address
// -------------------------------------- // i2c_scanner // // Version 1 // This program (or code that looks like it) // can be found in many places. // For example on the Arduino.cc forum. // The original author is not know. // Version 2, Juni 2012, Using Arduino 1.0.1 // Adapted to be as simple as possible by Arduino.cc user Krodal // Version 3, Feb 26 2013 // V3 by louarnold // Version 4, March 3, 2013, Using Arduino 1.0.3 // by Arduino.cc user Krodal. // Changes by louarnold removed. // Scanning addresses changed from 0...127 to 1...119, // according to the i2c scanner by Nick Gammon // http://www.gammon.com.au/forum/?id=10896 // Version 5, March 28, 2013 // As version 4, but address scans now to 127. // A sensor seems to use address 120. // Version 6, November 27, 2015. // Added waiting for the Leonardo serial communication. // // // This sketch tests the standard 7-bit addresses // Devices with higher bit address might not be seen properly. // #include <Wire.h> void setup() { Wire.begin(); Serial.begin(9600); while (!Serial); // Leonardo: wait for serial monitor Serial.println("\nI2C Scanner"); } void loop() { byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 1; address < 127; address++ ) { // The i2c_scanner uses the return value of // the Write.endTransmisstion to see if // a device did acknowledge to the address. Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) Serial.print("0"); Serial.print(address,HEX); Serial.println(" !"); nDevices++; } else if (error==4) { Serial.print("Unknown error at address 0x"); if (address<16) Serial.print("0"); Serial.println(address,HEX); } } if (nDevices == 0) Serial.println("No I2C devices found\n"); else Serial.println("done\n"); delay(5000); // wait 5 seconds for next scan }
資料來源:
https://playground.arduino.cc/Main/I2cScanner
[Arduino] millis() 使用注意事項
最近在在Arduino上每隔五分鐘要用MQTT傳一次資料給SERVER,所以
用millis() 這個函式來取得arduino執行程式到目前過了幾個milliseconds
但這個值是用unsign long (32bit) 儲存的,所以大概在約50天左右就會overflow,
當五十天後,你就永遠不會傳送資料了,因為millis()可能會遠小於sendtime。等到大於sendtime時,差不多又overflow了。
但是以下的寫法是不會有問題的
我們先來了解一下unsigned long 到底可以放的下什麼
0 to 4,294,967,295 (2^32 - 1)
那如果overflow 後會發生什麼事
輸出結果如下,當大於2^32 -1 後,就會歸零。
number is :4294967295
number is :0
我們來模擬一下overflow之後的運算
number is :4294967295
number+1 is :0
number- 2^32-1 :1
參考資料:
millis() 溢出(overflow)歸零(rollover)有沒問題?(教程)定时器相關http://www.arduino.cn/thread-12506-1-1.html
用millis() 這個函式來取得arduino執行程式到目前過了幾個milliseconds
但這個值是用unsign long (32bit) 儲存的,所以大概在約50天左右就會overflow,
從0開始,但是如果程式寫法是如下,那就好笑了。
unsigned long start=millis(); int delaytime=3000; void loop() { while (millis()<start+delaytime) { ; } sendData(); }
當五十天後,你就永遠不會傳送資料了,因為millis()可能會遠小於sendtime。等到大於sendtime時,差不多又overflow了。
但是以下的寫法是不會有問題的
unsigned long start=millis(); unsigned long delay=3000; if (millis()-start>delay) { sendData(); start=millis(); }
我們先來了解一下unsigned long 到底可以放的下什麼
0 to 4,294,967,295 (2^32 - 1)
那如果overflow 後會發生什麼事
unsigned long number=4294967295; void setup() { // put your setup code here, to run once: Serial.begin(9600); Serial.println("number is :"+String(number)); number+=1; Serial.println("number+1 is :"+String(number)); }
輸出結果如下,當大於2^32 -1 後,就會歸零。
number is :4294967295
number is :0
我們來模擬一下overflow之後的運算
unsigned long number=4294967295; unsigned long testNumber=4294967295; void setup() { // put your setup code here, to run once: Serial.begin(9600); Serial.println("number is :"+String(number)); number+=1; Serial.println("number+1 is :"+String(number)); Serial.println("number- 2^32-1 :"+String(number-testNumber)); }
number+1 is :0
number- 2^32-1 :1
所以0-(2^32-1) 會是1 ,
以此類推,當overflow後,即使millis()比較小,相滅之後仍然會是正值。
number is :4294967295
number+10000 is :9999
number- 2^32-1 :10000
我們來測試一下,若是millis()比較小時做相減會怎樣
number is :0
number+10000 is :10000
number- 2^32-1 :10001
所以2數運算時要考慮到unsigned long 能裝的下的空間,unsigned long 面對運算結果負值也會出現問題。
number is :0
number+10000 is :10000
number-10001 :4294967295
millis() 溢出(overflow)歸零(rollover)有沒問題?(教程)定时器相關http://www.arduino.cn/thread-12506-1-1.html
「Arduino」MQTT踩到一個坑
紀錄一下使用MQTT時採到的一個坑,在爬回平地前先來
看看Pubsubclient lib裡面一個基本的範例 ,不可否認的,這範例寫的很清楚。
我的MQTT Broker是mosquitto 1.4.x 版
單一台裝置運作時,非常順利,但是同樣的程式碼,在二個arduino上執行時會出現
漏訊息,而且拼命和MQTT Broker Reconnect問題。
查詢了一下錯誤代碼,rc=-4表示 Connection Timeout , 在MQTT中,client與server之間會
有一個keepalive的參數,這個lib預設是15秒,也就是在15內,client有義務和server保持連
接,當超過keepalive所設定的秒數內沒有傳送資料,client必須發送一個心跳訊號,而server
在收到心跳訊號後,必須回覆,這個client才能確認與server是保持連通的。

後來確認後發現是這一行程式碼導致的問題
一眼瞄過去我也不覺得那裡有異,直到去查詢後才發現"arduinoClient" 其實是
ClientID , 所以不應該重覆,否則Server會無法正確識別與client連線狀態,導致Client
可能沒有正確收到Server response 的keepalive回覆,誤以為斷線。
看看Pubsubclient lib裡面一個基本的範例 ,不可否認的,這範例寫的很清楚。
我的MQTT Broker是mosquitto 1.4.x 版
#include <SPI.h> #include <Ethernet.h> #include <PubSubClient.h> // Update these with values suitable for your network. byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED }; IPAddress ip(172, 16, 0, 100); IPAddress server(172, 16, 0, 2); void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i=0;i<length;i++) { Serial.print((char)payload[i]); } Serial.println(); } EthernetClient ethClient; PubSubClient client(ethClient); void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Attempt to connect if (client.connect("arduinoClient")) { Serial.println("connected"); // Once connected, publish an announcement... client.publish("outTopic","hello world"); // ... and resubscribe client.subscribe("inTopic"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } void setup() { Serial.begin(57600); client.setServer(server, 1883); client.setCallback(callback); Ethernet.begin(mac, ip); // Allow the hardware to sort itself out delay(1500); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); }
單一台裝置運作時,非常順利,但是同樣的程式碼,在二個arduino上執行時會出現
漏訊息,而且拼命和MQTT Broker Reconnect問題。
查詢了一下錯誤代碼,rc=-4表示 Connection Timeout , 在MQTT中,client與server之間會
有一個keepalive的參數,這個lib預設是15秒,也就是在15內,client有義務和server保持連
接,當超過keepalive所設定的秒數內沒有傳送資料,client必須發送一個心跳訊號,而server
在收到心跳訊號後,必須回覆,這個client才能確認與server是保持連通的。
後來確認後發現是這一行程式碼導致的問題
if (client.connect("arduinoClient"))
一眼瞄過去我也不覺得那裡有異,直到去查詢後才發現"arduinoClient" 其實是
ClientID , 所以不應該重覆,否則Server會無法正確識別與client連線狀態,導致Client
可能沒有正確收到Server response 的keepalive回覆,誤以為斷線。
2017年5月11日 星期四
[Arduino] Arduino Nano + ENC28J60 LED問題
最近使用Arduino Nano V3 搭配ENC28J60 Ethernet Shield Network Module
For Arduino Nano 時遇到Pin 13 LED 沒反應問題。

+
For Arduino Nano 時遇到Pin 13 LED 沒反應問題。

+
研究後發現,在Ethernet.begin(mac);後, Pin 13 LED就失去反應了。
上網爬文後,得到解答,我使用UIPEthernet Library , ENC28J60使用SPI協定與
Arduino溝通,SPI使用的腳位分別是D10~D13,所以13 Pin除了是LED之外,也作為SCK
用途,被佔用了,當然LED就不會亮囉。
Pin name | ETHER_28J60 | Ethercard | UIPEthernet | My eBay Module |
---|---|---|---|---|
SS | 10 | 8 (!) | 10 | 10 |
MOSI (SI) | 11 | 11 | 11 | 11 |
MISO (SO) | 12 | 12 | 12 | 12 |
SCK | 13 | 13 | 13 | 13 |
表格來源:
[Arduino] Linkit ONE 小筆記
最近在公司接觸到Linkit ONE,碰到一些小問題,筆記一下。
1. Linkit ONE只有二支腳支援PWM(D3及D9),而不像Arduino一樣有六支。
2. Linkit ONE在接上電腦後會有二個Comport。只有一個可以顯示Serial.print的內容。
3 .Linkit ONE 的PWM是13 bit的解析度,範圍由0~8191,電壓為0~3.3V。
4. LinkIt ONE在連上WI-FI後要稍等一下,讓它有時間取得IP,不要一連線就急著連網。
1. Linkit ONE只有二支腳支援PWM(D3及D9),而不像Arduino一樣有六支。
2. Linkit ONE在接上電腦後會有二個Comport。只有一個可以顯示Serial.print的內容。
3 .Linkit ONE 的PWM是13 bit的解析度,範圍由0~8191,電壓為0~3.3V。
4. LinkIt ONE在連上WI-FI後要稍等一下,讓它有時間取得IP,不要一連線就急著連網。
2017年3月26日 星期日
[Arduino]RS485 初嘗
買了一塊WaveShare 的UART轉RS485(5V)板子來玩玩看。
先簡介一下RS485:
EIA-485(過去叫做RS-485或者RS485])是隸屬於OSI模型實體層的電氣特性規定為2線,半雙工,多點通訊的標準。它的電氣特性和RS-232不大一樣。用纜線兩端的電壓差值來表示傳遞訊號,1極的電壓標識為邏輯1,另一段標識為邏輯0。兩端的電壓差最小為0.2V以上時有效,任何不大於12V或者不小於-7V的差值對接受端都被認為是正確的。
EIA-485僅僅規定了接受端和傳送端的電氣特性。它沒有規定或推薦任何資料協定。EIA-485可以應用於配置便宜的廣域網路和採用單機傳送,多機接受通訊連結。它提供高速的資料通訊速率(10m時35Mbit/s;1200m時100kbit/s)。EIA-485和EIA-422一樣使用雙絞線進行高電壓差分平衡傳輸,它可以進行大面積長距離傳輸(超過4000英尺,1200公尺)。
EIA-485經常和常用裝置UART一起使用來實現在飛機上的低速率資料傳輸,舉個例子,一些乘客控制單元採用這種裝置,從而只需要很少的線纜就可以實現幾端子共享線纜,從而減輕整個裝置的重量。
EIA-485 WIKI
主要特點為RSE這一支腳,它控制了資料流的方向,當RSE為HIGH時,代表要送資料,
RSE為LOW時表示要接收資料,所以一開始Master會預設為發送,remote會預設為接收。
先簡介一下RS485:
EIA-485(過去叫做RS-485或者RS485])是隸屬於OSI模型實體層的電氣特性規定為2線,半雙工,多點通訊的標準。它的電氣特性和RS-232不大一樣。用纜線兩端的電壓差值來表示傳遞訊號,1極的電壓標識為邏輯1,另一段標識為邏輯0。兩端的電壓差最小為0.2V以上時有效,任何不大於12V或者不小於-7V的差值對接受端都被認為是正確的。
EIA-485僅僅規定了接受端和傳送端的電氣特性。它沒有規定或推薦任何資料協定。EIA-485可以應用於配置便宜的廣域網路和採用單機傳送,多機接受通訊連結。它提供高速的資料通訊速率(10m時35Mbit/s;1200m時100kbit/s)。EIA-485和EIA-422一樣使用雙絞線進行高電壓差分平衡傳輸,它可以進行大面積長距離傳輸(超過4000英尺,1200公尺)。
EIA-485經常和常用裝置UART一起使用來實現在飛機上的低速率資料傳輸,舉個例子,一些乘客控制單元採用這種裝置,從而只需要很少的線纜就可以實現幾端子共享線纜,從而減輕整個裝置的重量。
以上資料來源:
簡單來說,RS485並沒有規定使用任何界面,只要電氣特性相符即可。
最左邊是接UART , 你可以看到它提供3種介面的RS485 A/B ,除了杜邦線之外就是2P端子 ,以及一個
腳立說明如下,資料來源:
主要特點為RSE這一支腳,它控制了資料流的方向,當RSE為HIGH時,代表要送資料,
RSE為LOW時表示要接收資料,所以一開始Master會預設為發送,remote會預設為接收。
由於RS485是半雙工的通訊方式,所以每一個裝都只能是傳送或是接收其中一種狀態。
參考了以下網址的範例。
https://arduino-info.wikispaces.com/SoftwareSerialRS485Example
這個範例master每次只能傳送一個byte,然候就會切換到接收模式直到Serial有再次輸入,就會切回去發送模式(此時remote傳再多它也收不到了),而remote在接收到資料後會切換成傳送模式把資料發送回去,10ms後就會再回到接收模式。
這個範例master每次只能傳送一個byte,然候就會切換到接收模式直到Serial有再次輸入,就會切回去發送模式(此時remote傳再多它也收不到了),而remote在接收到資料後會切換成傳送模式把資料發送回去,10ms後就會再回到接收模式。
接腳如下:
arduino RS485
10 RX
11 TX
3 RSE
5V VCC
GND GDN
二塊板子之間的A接A,B接B。
之後燒錄程式,一邊為Master,一邊為remote
Master端程式如下:
remote端如下
程式碼來源:(我有稍微做一點修改)
https://arduino-info.wikispaces.com/SoftwareSerialRS485Example
master執行畫面(記得選擇為沒有結尾)
remote執行畫面(使用CoolTerm)
記得先修改一下傳送的結尾模式
由master傳送一個字元a,會收到一個相同回應
remote端也會將收到的字元顯示並回傳給Master
如果一次輸入一堆字傳送,可能就會出現問題,例如我輸入1234567890abcdefg
remote端收到的也是不正確的字元
所以使用rs485做傳輸時,一定要注意收和送的時間點,建議使用現有的協定,例如ModBus,因為在收和送之間需要切換,這個如果沒有搭配好,會出現異常的資料。
Master端程式如下:
/* YourDuino SoftwareSerialExample1 - Connect to another Arduino running "YD_SoftwareSerialExampleRS485_1Remote" - Connect this unit Pins 10, 11, Gnd - Pin 3 used for RS485 direction control - To other unit Pins 11,10, Gnd (Cross over) - Open Serial Monitor, type in top window. - Should see same characters echoed back from remote Arduino Questions: terry@yourduino.com */ /*-----( Import needed libraries )-----*/ #include <SoftwareSerial.h> /*-----( Declare Constants and Pin Numbers )-----*/ #define SSerialRX 10 //Serial Receive pin #define SSerialTX 11 //Serial Transmit pin #define SSerialTxControl 3 //RS485 Direction control #define RS485Transmit HIGH #define RS485Receive LOW #define Pin13LED 13 /*-----( Declare objects )-----*/ SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX /*-----( Declare Variables )-----*/ int byteReceived; int byteSend; void setup() /****** SETUP: RUNS ONCE ******/ { // Start the built-in serial port, probably to Serial Monitor Serial.begin(9600); Serial.println("YourDuino.com SoftwareSerial remote loop example"); Serial.println("Use Serial Monitor, type in upper window, ENTER"); pinMode(Pin13LED, OUTPUT); pinMode(SSerialTxControl, OUTPUT); digitalWrite(SSerialTxControl, RS485Receive); // Init Transceiver // Start the software serial port, to another device RS485Serial.begin(4800); // set the data rate }//--(end setup )--- void loop() /****** LOOP: RUNS CONSTANTLY ******/ { digitalWrite(Pin13LED, HIGH); // Show activity if (Serial.available()) { byteSend = Serial.read(); digitalWrite(SSerialTxControl, RS485Transmit); // Enable RS485 Transmit RS485Serial.write(byteSend); // Send byte to Remote Arduino digitalWrite(Pin13LED, LOW); // Show activity delay(10); digitalWrite(SSerialTxControl, RS485Receive); // Disable RS485 Transmit } if (RS485Serial.available()) //Look for data from other Arduino { digitalWrite(Pin13LED, HIGH); // Show activity byteReceived = RS485Serial.read(); // Read received byte if (byteReceived!=0 && byteReceived!=10 && byteReceived!=13) { Serial.print("receive:"); // Show on Serial Monitor Serial.write(byteReceived); Serial.println(); } delay(10); digitalWrite(Pin13LED, LOW); // Show activity } }//--(end main loop )--- /*-----( Declare User-written Functions )-----*/ //NONE //*********( THE END )***********
remote端如下
/* YourDuino SoftwareSerialExample1Remote - Used with YD_SoftwareSerialExampleRS485_1 on another Arduino - Remote: Receive data, loop it back... - Connect this unit Pins 10, 11, Gnd - To other unit Pins 11,10, Gnd (Cross over) - Pin 3 used for RS485 direction control - Pin 13 LED blinks when data is received Questions: terry@yourduino.com */ /*-----( Import needed libraries )-----*/ #include <SoftwareSerial.h> /*-----( Declare Constants and Pin Numbers )-----*/ #define SSerialRX 10 //Serial Receive pin #define SSerialTX 11 //Serial Transmit pin #define SSerialTxControl 3 //RS485 Direction control #define RS485Transmit HIGH #define RS485Receive LOW #define Pin13LED 13 /*-----( Declare objects )-----*/ SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX /*-----( Declare Variables )-----*/ int byteReceived; int byteSend; void setup() /****** SETUP: RUNS ONCE ******/ { // Start the built-in serial port, probably to Serial Monitor Serial.begin(9600); Serial.println("SerialRemote"); // Can be ignored pinMode(Pin13LED, OUTPUT); pinMode(SSerialTxControl, OUTPUT); digitalWrite(SSerialTxControl, RS485Receive); // Init Transceiver // Start the software serial port, to another device RS485Serial.begin(4800); // set the data rate }//--(end setup )--- void loop() /****** LOOP: RUNS CONSTANTLY ******/ { //Copy input data to output if (RS485Serial.available()) { byteSend = RS485Serial.read(); // Read the byte digitalWrite(Pin13LED, HIGH); // Show activity delay(10); digitalWrite(Pin13LED, LOW); Serial.write(byteSend); digitalWrite(SSerialTxControl, RS485Transmit); // Enable RS485 Transmit if (byteSend!=0 && byteSend!=10 && byteSend!=13) RS485Serial.write(byteSend); // Send the byte back delay(10); digitalWrite(SSerialTxControl, RS485Receive); // Disable RS485 Transmit // delay(100); }// End If RS485SerialAvailable }//--(end main loop )--- /*-----( Declare User-written Functions )-----*/ //NONE //*********( THE END )***********
程式碼來源:(我有稍微做一點修改)
https://arduino-info.wikispaces.com/SoftwareSerialRS485Example
master執行畫面(記得選擇為沒有結尾)
remote執行畫面(使用CoolTerm)
記得先修改一下傳送的結尾模式
由master傳送一個字元a,會收到一個相同回應
remote端也會將收到的字元顯示並回傳給Master
如果一次輸入一堆字傳送,可能就會出現問題,例如我輸入1234567890abcdefg
remote端收到的也是不正確的字元
所以使用rs485做傳輸時,一定要注意收和送的時間點,建議使用現有的協定,例如ModBus,因為在收和送之間需要切換,這個如果沒有搭配好,會出現異常的資料。
訂閱:
文章 (Atom)