Arduino基础知识复习12.18
文章目錄
- 前言
- 一. 原型技術中的編程基礎(Arduino C)
- a. 變量和數據類型
- 浮點型變量
- 數組
- String
- C字符串
- 比較和位運算
- *復合運算和賦值
- *字符串和數字的互相轉化
- b. 程序結構控制
- 順序
- 選擇
- 循環
- c. 函數
- d. 庫的使用
- 內置庫
- 創建自己的庫
- 二. 物理原型開發基礎(Arduino)
- a. 交互式硬件原型的開發基礎:電流、常?電?元件、示意圖與電路圖,萬?表、焊接、 ?包板的使?。
- b. 串?通信
- 串口(UART)
- 硬件串口
- 串口通訊的函數
- 軟件串口
- 多個串口
- c. 簡單數字和模擬輸?
- 開關
- 按鍵消抖
- 數字鍵盤
- 模擬輸入
- *多路復用模擬輸入
- 測量電壓
- d. 獲取傳感器輸?
- 動作(數字量高低電平,類似開關)
- 動作檢測(集成被動紅外探測器PIR,只需判斷模擬量高電平)
- 光(光敏電阻模擬量)
- 距離(超聲波,輸出模式純凈高電平+輸入模式計算脈沖時間)
- 精確測距(*紅外IR傳感器比超聲波精度高但量程小,輸出非線性需要查表)
- 振動(壓電傳感器/爆震傳感器,應力越大電壓越高。模擬量)
- 聲音
- 溫度
- *RFID標簽
- *旋轉動作(旋轉編碼器)
- *鼠標
- *GPS
- *加速度(陀螺儀測量角加速度,加速度計測量移動加速度如重力加速度)
- e. 可視輸出、物理輸出、聲?輸出
- 顯示
- 電機
- 音頻
- f. ?線通信和?絡
- * I2C和SPI
- 無線XBEE
- 藍牙
- *互聯網
- 三. 上述技術在設計原型制作和開發中的應?。
- 1.接收到____信號時,_____動作
- 2.智能水杯(傳感器、顯示器、LED、蜂鳴器、藍牙等等綜合運用)
前言
含星號*的不太常用。閱讀前最好有一點基礎知識,可以看個視頻入門一下。本文參考書為Arduino Cookbook(Michael Margolis 等著)中譯本《Arduino權威指南》。Arduino是開放的電子開發平臺,包含硬件和軟件。
常用的一種硬件Arduino UNO如下圖:
記:
左邊5V輸出、GND引腳, 0-5模擬引腳
右邊USB接口 GND 13-0數字引腳(3 5 6 9 10 11支持PWM)
模擬引腳帶ADC功能,10位精度,analogRead(pin),可將0~5V的電壓轉換為0 ~1023
PWM:脈沖寬度調制。analogWrite(pin,value); value值改變的是PWM的脈沖寬度,范圍為0~255
一. 原型技術中的編程基礎(Arduino C)
Arduino中的程序長這樣:
const int ledPin=13;//LED連接到數字引腳13 void setup() {pinMode(ledPin,OUTPUT);//以輸出方式初始化一個引腳 } void loop() {digitalWrite(ledPin,HIGH);//點亮LEDdelay(1000);//等待1000ms即1sdigitalWrite(ledPin,LOW);//熄滅LEDdelay(1000); }a. 變量和數據類型
| int | 2 | -32768~36767 | 整數值 |
| unsigned int | 2 | 0~65535 | 正整數值 |
| long | 4 | -2^16~ 2 ^16-1 | 大范圍整數 |
| unsigned long | 4 | 0~2^32-1 | 大范圍正整數 |
| float | 4 | 3.4028235E+38~-3.4028235E+38 | 帶小數部分的數字,7位有效十進制數字(32位存儲巨大范圍內的所有值,8位用于小數位置(指數),剩下24位留給符號和數值) |
| double | 4 | 同float | 在arduino里只是float的另一個名稱 |
| boolean | 1 | false(0)或true(1) | 代表true或false值 |
| char | 1 | -128~127 | 代表單個字符,也可以代表范圍內的有符號值 |
| Byte | 1 | 0~255 | 類似char,但無符號值 |
其他:
string:char陣列(字符),通常用來包含文本
void:僅用在無返回值的函數聲明
浮點型變量
/* 浮點例子 初始化浮點值至1.1,每次減少0.1至0 */ boolean almostEqual(float a, float b);//定義了一個布爾型返回值的函數“視作幾乎相等”, float value=1.1; void setup() {Serial.begin(9600); } void loop() {value=value-0.1;if(value==0)Serial.println("the value is exactly zero");else if(almostEqual(value,0))//比較此時value值和0的差距是否近似相等{Serial.print("The value");Serial.print(value,7);//打印到7位小數Serial.println("is almost equal to zero");}elseSerial.println(value);delay(100); }/*定義了一個布爾型返回值的函數“視作幾乎相等”,輸入形式參數為a和b,如果a和b之前差很小,則返回true把DELTA的值設成能視作相等的最大差值*/boolean almostEqual(float a, float b){const float DELTA=.00001;//視作幾乎相等的最大差值if (a==0) return fabs(b) <=DELTA;//fabs() 浮點絕對值if (b==0) return fabs(a) <=DELTA;//上面的if代碼是為了防止分母為0報錯return fabs((a-b)/max(fabs(a),fabs(b))) <= DELTA;//其實我感覺用這一句就夠了return fabs(a-b)<=DELTA;}數組
/* 數組程序 由開關陣列控制LED陣列 */int inputPins[]={2,3,4,5}; //為開關輸入創建引腳陣列 int ledPinds[]={10,11,12,13};//為LED輸出創建引腳陣列void setup() {for(int index=0;index<4;index++){pinMode(ledPins[index],OUTPUT);//聲明LED對應引腳為輸出模式pinMode(inputPins[index],);//聲明按鍵對應引腳為輸入模式digitalWrite(inputPins[index],HIGH);//按鍵引腳,允許上拉電阻} }void loop() {for(int index=x;index<4;index++){int val=digitaleRead(inputPins[index]);//讀取輸入值if(val=LOW)//檢查開關是否按下{digitalWrite(ledPins[index],HIGH);//如果按下就點對應燈}else{digitalWrite(ledPins[index],LOW);//熄滅LED}} }
要點:使用數字引腳,開關接GND,LED需要接電阻和GND
String
/*arduino字符串和c字符數組相比使用簡單,但存在內存泄露、占用問題。 */String text1="2只老虎"; String text2="愛跳舞"; String text3;//在程序內被賦值void setup() {Serial.begin(9600);Serial.print(text1);Serial.print("is");Serial.print(text1.length());//length()返回字符數Serial.println("characters long.");Serial.print("text2 is");Serial.print(text2.length());Serial.println("characters long.");text1.concat(text2);Serial.println("用了text1.concat(text2);后text1變為");Serial.println(text1); //加法運算符可以結合字符串text3=text1+text2;Serial.println("用了text3=text1+text2;后text3=");Serial.println(text3);}void loop(){}
C字符串
//strcpy(destination,source);//復制字符串從source到de
//strncpy(destination,source,6);//復制6個字符從source到de
//strcat(destination,source);//復制字符串從source到de的結尾
String text1=“Arduino”;
char stringA[8];//聲明長達7個字符加上終止空字符的字符串
char stringB[8]=“Arduino”;//同上,并初始化
char stringC[16]=“Arduino”;//同上,但字符串有加長的空間
char stringD[]=“Arduino”;//由編譯器初始化串并計算大小
* text1.concat(stringC);可以兼容,但+不能兼容char和string
比較和位運算
1.字符和數量值
比較相等關系
== != < > >= <=
if (strcmp(string1,string2)==0) //判斷相等則返回0值,第一個大返回值大于零,否則小于0 //*可以在string2后加“,4”指定判斷4個字符Serial.print("相等");2.邏輯比較
&&與 ||或 !非
3.位運算
&按位與,二進制數,如果兩個位都是1,才設置運算后的那個位為1,如3&1等于1(11&01=01)
|按位或,對應的兩個位有1得1,3|1=11(11|01=11)
^按位異或,對應的兩個位是一個1才設置1,如5 ^ 4 =2(110 ^ 100=010)
~取反,反轉每個位(比特bit)的值,如 ~ 1 =254(~00000001=11111110)
4.余數
%
5.其他常用計算函數
abs(x)計算x的絕對值 constrain(x,min,max)返回一個在min和max范圍內的值 min(x,y) max(x,y)
返回最值 pow(x,y) 計算x的y次方 sqrt(x) x的平方根 floor(x) 向下取整,不大于x的最大整數值
ceil(x)向上取整,不小于x的最小整數值 sin(x) cos(x) tan(x)
*設置和讀取位
最右邊是最低位 bitSet(x,bitPosition) 設置 寫入1 bitClear(x,bitPosition)清除 寫入0
bitRead(,)返回指定位的值 bitWrite(x,bitPosition,value)
設置x的第bitPosition位為value值(0或1) bit(bitPosition)返回給定的比特位置的值 bit(0)=1
bit (1)=2 bit(2)=4
所以8個開關的狀態可以包裝成一個單一的8位值,而不需要8個字節或整數 int整數值剛好由2個字節,16個位組成。
取高低字節就是取高8位低8位:
本質上是位運算,所以可以自己定義這樣的函數如下
//w為32位數,取高16位就是把整體右移16位。宏表達式為 #define highWord(w) ((w)>>16) //取低16位就是把高16位變成0,可以用高16位為0的數去 與運算 #define lowWord(w) ((w) & 0xffff )*從高字節和低字節組合成一個int或long值
word(high,low)把高低字節組裝成一個16位值(2字節)
*復合運算和賦值
| += 或-=和*=或/= | value+=5;//本身加5,用于遞增或遞減 |
| >>=或<<= | value>>=2;//向右移動2位 |
| &=或 |= | value&=2;//用2對value做二進制 與屏蔽 |
*字符串和數字的互相轉化
/* *1.從給定數字創建一個字符串 itoa 從整數到ASCII碼,Itoa長整數到ASCII碼 */void setup() {Serial.begin(9600);}char buffer1[12];//long數據類型有11個字符 包括減號和末尾空字符void loop(){long value=15;itoa(value,buffer1,2);//要轉的值value,將要存放輸出字符串的緩沖區為buffer1,2進制// Serial.print(value);Serial.print(buffer1);Serial.print("為轉換的二進制值,有");Serial.print(strlen(buffer1));Serial.print("位數,");value=23333;itoa(value,buffer1,16);Serial.print(value);Serial.print("轉為16進制數");Serial.print(buffer1);Serial.print("有");Serial.print(strlen(buffer1));Serial.println("位數");delay(1000);}//結果:1111為轉換的二進制值,有4位數,23333轉為16進制數5b25有4位數 /* *2.從給定字符串創建一個數字 */ const int ledPin=LED_BUILTIN;//LED連接的管腳、 int blinkDelay;//閃爍周期 char strValue[6]; int index=0;//存儲所接收的數字的數字索引 void setup() {Serial.begin(9600);pinMode(ledPin,OUTPUT);//LED引腳為輸出}void loop(){if(Serial.available()){char ch=Serial.read();//讀取電腦串口發來的文字為ASCII字符Serial.print(ch);if(index<4 && isDigit(ch)){//緩沖區未滿而且ch是數字字符strValue[index++]=ch;//在字符串里加上ASCII字符}else{//這里是當緩沖區滿或者遇到第一個非數字strValue[index]=0;blinkDelay=atoi(strValue);//使用atoi將字符串轉換為int,賦值給閃爍周期值index=0;}}blink();}void blink() {digitalWrite(ledPin,HIGH);delay(blinkDelay/2);//等待閃爍周期的一半digitalWrite(ledPin,LOW);delay(blinkDelay/2);//等待閃爍周期的一半}//這個程序存在的缺陷是一旦開始閃爍了,delay期間就不會執行任何串口的監測b. 程序結構控制
順序
選擇
1.if
if()
{
語句;
}
2.switch
只能整型或者字符型
switch(字符變量) {case '1':函數1;break;case '+':函數2;default:函數3break; }循環
1.while
while(條件){} do { } while(條件);2.for
for(int i=0;i<4;i++){ ...}3.跳出循環
break;
continue;
c. 函數
int blink3(int period)
{…}
返回類型 函數名 (參數類型 參數名字)
需要函數返回多個值可以在最開始定義全局變量,然后在函數中改變全局變量。也可以使用引用,符號&表示該參數是引用,函數內值的變化也將改變調用該函數時被賦值的變量的值。如下:
void swap(int &value1,int &value2)
返回類型 函數名 (參數類型 &參數名字)
函數聲明:告知編譯器你要引用函數,出現在頂部,結束加分號如下:
void swap(int &value1,int &value2);
常用函數先略,后面出現再介紹。
d. 庫的使用
內置庫
#include <Servo.h>//頭文件在庫的文件夾
#include “Servo.h”//頭文件在當前文件夾
一些常用的庫:SoftwareSerial軟串口
SPI以太網和SPI硬件
Wire I2C設備
Stepper 步進電機的工作
*其他:
EEPROM內存 Ethernet以太網通訊 FIrmata簡化串行通訊和板卡控制的協議
LiquidCrystal 液晶顯示器 SD支持使用外部硬件讀取和寫文文件到SD卡
Matrix 管理LED陣列
Sprite 激活與LED陣列…
創建自己的庫
*可以先不看
#開頭的語句被稱為預處理命令
#define 標識符字符串 宏定義
#include 文件包含 條件編譯 用來防止重復定義
#ifndef 標識符 程序段
#endif
#if 表達式 程序段1
#else 程序段2
#endif
/* 創建一個 blinkLED.cpp 一個以給定的毫秒數時間點亮LED的簡單庫
*/
#include “Arduino.h”//arduin函數和常數的庫所需要的
#include “blinkLED.h”//包含了你庫的函數定義
//在給定的引腳以給定的時間閃爍LED void blinkLED(int pin, int duration) {
digitalWrite(pin, HIGH); //打開LED delay(duration);
digitalWrite(pin, LOW); //關閉LED delay(duration); }
/* 再創建一個blinkLED.h要放在和cpp文件一起的文件夾*/
#include “Arduino.h” void blinkLED(int pin, int duration);//函數原型
//最后在blinkLibTest程序中#include “blinkLED.h”
關鍵字高亮顯示需要再建立一個keywords.txt文件,鍵入:你的關鍵詞 KEYWORD1
二. 物理原型開發基礎(Arduino)
a. 交互式硬件原型的開發基礎:電流、常?電?元件、示意圖與電路圖,萬?表、焊接、 ?包板的使?。
面包板原理圖
從原理圖可以看到,面包板上下區是橫向5位相通,一般用于接電源和接地,中間區域是縱向5位相通,通常用于放置電路元件和電路連接線。
電阻:對電流起阻礙作用的元件。
電容:裝電的容器,旁路、去耦、濾波、儲能作用。
二極管:單項傳導電流,整流、穩壓作用。
三級管:放大、振蕩、開關作用。EBC三級,有兩種是PNP和NPN。
繼電器:可控電子開關,原理電磁效應控制線圈。
傳感器
電阻類:光敏電阻(光亮電阻降)
人體熱釋電紅外傳感器:對人體輻射出的紅外線敏感,無人在監測范圍內輸出低電平,有人輸出高電平脈沖信號。
溫度傳感器
五相傾斜:內部由一個金屬球和4個觸點組成,檢測4個傾斜方向和水平位置共5種狀態。
觸摸模塊:電容觸摸感應原理檢測人體接觸,有人高無人低。
模擬聲音傳感器:檢測周圍環境聲音的大小
氣體傳感器:使用氣敏材料是在清潔空氣中電導率較低的二氧化錫,當傳感器所處環境中存在可燃氣體時,傳感器的電導率隨著空氣中可燃氣體濃度的增加而增大。使用簡單的電路就可以將電導率的變化轉換為與該氣體濃度相對應的輸出信號。
時鐘模塊:雖然arduino的millis()和micro()可以獲取運行時間,但是到一定時間就會溢出,而且斷電不保存,可以用時鐘模塊準確、長時間計時。
顯示器
b. 串?通信
串口(UART)
一種信息交互方式,硬件提供Arduino和它正在與之通信的設備之間的電信號,軟件使用硬件發送所連接的硬件可以理解的字節或位。
硬件位于0(RX) 1(TX)兩個引腳。
Arduino的串口庫隔離了大部分硬件的復雜性,比較容易使用。
硬件串口
原理:串口硬件的發送和接收表示序列位的電脈沖數據。Arduino使用TTL電平表示構成字節信息的0和1,用0V表示比特率0 , 5V(或3.3V)表示比特值1。
大多數arduino板子有一個芯片把硬件串口轉換成通用串口總線USB,用于連接到其他硬件串口。有的設備使用RS-232標準進行串口連接,通常有9針連接器,是一種舊的通訊協議,采用的電壓與arduino不兼容。
Arduino Mega有4個硬件串口,可以與4個串口設備進行通訊,其中一個有USB接口
串口通訊的函數
1.初始化
Serial.begin(9600);//發送和接受的波特率范圍是300~115200。9600,即每秒鐘傳輸9600個“位元”(比特bit)
2.輸出
Serial.print() 打印完不換行
Serial.println(“char1”);
Serial.println(char1); 打印完換行
Serial.println(char1,HEX);//HEX是16進制形式輸出 OCT是10進制 BIN是二進制
(*print和write的區別,print發送的不是數據本身而是轉換為字符,然后將字符對應的ASCII碼發出去,串口監視器收到ASCIII碼就會顯示對應的字符,而write發送的是數值本身,串口監視器收到后會把數值當做ASCII碼而顯示對應的字符)
3.輸入
Serial.read()
調用該函數時,arduino會從64B的緩沖區中取出1B的數據
4.Serial.available();
返回值就是當前緩沖區中接收到的數據字節數
*串口事件 void serialEvent(){} 當串口緩沖區中有數據,就會觸發該事件。
*可以利用C++的流插入語法和模板,如果聲明了一個流模板就可以使用它。
可以簡化為 Serial<< “At” << t <<“seconds,speed=”<< s <<", distance =" << d << endl;
舉一個USB串口通訊的實例(實際上在前面已經涉及過了)
/* 打印數字到串口監視器 */ void setup() {Serial.begin(9600);//發送和接受的波特率范圍是300~115200。9600,即每秒鐘傳輸9600個“位元”(比特bit) } int number=0; void loop() {Serial.print("The number is ");Serial.println(number);//打印數字并換行delay(500);number++; }也可以再復習一下這個用Serial.read接收電腦從串口傳來的數據,然后轉為數字,改變LED閃爍周期的。
接受單個數字的
復雜一點的,要用C語言轉換函數atoi或atol把文本轉化為數字
/* *從給定字符串創建一個數字 */ const int ledPin=LED_BUILTIN;//LED連接的管腳、 int blinkDelay;//閃爍周期 char strValue[4];//最多包含4位數字 int index=0;//存儲所接收的數字的數字索引 void setup() {Serial.begin(9600);pinMode(ledPin,OUTPUT);//LED引腳為輸出}void loop(){if(Serial.available()){char ch=Serial.read();//讀取電腦串口發來的文字為ASCII字符Serial.print(ch);if(index<4 && isDigit(ch)){//緩沖區未滿而且ch是數字字符strValue[index++]=ch;//在字符串里加上ASCII字符}else{//這里是當緩沖區滿或者遇到第一個非數字strValue[index]=0;blinkDelay=atoi(strValue);//使用atoi將字符串轉換為int,賦值給閃爍周期值index=0;}}blink();}//這個程序存在的缺陷是一旦開始閃爍了,delay期間就不會執行任何串口的監測 void blink() {digitalWrite(ledPin,HIGH);delay(blinkDelay/2);//等待閃爍周期的一半digitalWrite(ledPin,LOW);delay(blinkDelay/2);//等待閃爍周期的一半}可以學習一下處理思路,實戰可以直接用Serial.parseInt()或Serial.parseFloat()讀取串口字符,并返回表示的數值
/* *parseInt方法從給定字符串創建一個數字 */ const int NUMBER_OF_FIELDS =3;//預計有多少個逗號分割的字段 int fieldIndex=0;//接受的當前字段 int values[NUMBER_OF_FIELDS];//保存所有字段數值的數組void setup() {Serial.begin(9600);}void loop(){if(Serial.available()){for(fieldIndex=0;fieldIndex<3;fieldIndex++){values[fieldIndex]=Serial.parseInt();//獲取一個數值}Serial.print(fieldIndex);Serial.println("fields received:");for(int i=0;i<fieldIndex;i++){Serial.println(values[1]);}}}發送多個文本最簡單的方法就是字符分割
軟件串口
一個標準的arduino有一個硬件串口,但你也可以用軟件庫來模擬附加的端口(通信通道),以連接一個以上的設備,即軟件串口,它需要耗費內存資源,所以速度效率不如硬件串口。
程序示例:
#include <softwareSerial.h>
const int rxpin=2;//用于接收LCD的引腳 const int txpin=3;//用于傳送到LCD顯示屏的引腳 SoftwareSerial
serial_lcd(rxpin,txpin);//新的串口為引腳2個和3個
void setup(){serial_lcd.begin(9600);}
多個串口
1.只有RX接收腳的LCD
/*
在同一時間將數據發送到兩個串口設備,硬件串口
*/
//void setup(){ // 初始化mega的兩個串口
Serial.begin(9600);//主串口
Serial1.begin(9600);//mega也可以用serial1到serial3 }
/*
SoftwareSerialOutput
輸出數據到軟件串口
*/
#include <softwareSerial.h>
const int rxpin=2;//用于接收LCD的引腳
const int txpin=3;//用于傳送到LCD顯示屏的引腳
SoftwareSerial serial_lcd(rxpin,txpin);//新的串口為引腳2個和3個
void setup()
{
Serial.begin(9600);
serial_lcd.begin(9600);//初始化軟件串口也為9600b/s
}
int number=0;
void loop(){
serial_lcd.print("The number is ");//文本發送到LCD
serial_lcd.print(number);
Serial.print("the number is ");//在PC控制臺打印數字
Serial.print(number);
delay(1000);
number++;
}
GPS同理,
區別是軟串口的波特率改為4800
loop中改為這一段
c. 簡單數字和模擬輸?
開關
if(digitalRead(inputPin)=HIGH)
{digitalWrite(ledPin,HIGH)}
arduino內部有上拉電阻,可以不用外部電阻,程序需要修改一下
按鍵消抖
本程序針對下拉電阻,按鈕按下輸入arduino高電平
const int inputPin=2;//輸入引腳號 const int ledPin=13;//輸出引腳號 const int debounceDelay=10;//穩定之前循環等待的次數//按鍵穩定閉合debounceDelay時間,則返回true boolean debounce(int pin) {boolean state;//用于記錄本次狀態boolean previousState;previousState=digitalRead(pin);for(int counter=0;counter<debounceDelay;counter++){delay(1);//每次延時1msstate=digitalRead(pin);if(state!=previousState)//按鍵狀態發生變化{counter=0;//計數器重新計數previousState=state;}}//按鍵狀態不變return state;}void setup(){pinMode(inputPin,INPUT);pinMode(ledPin,OUTPUT); }void loop(){if(debounce(inputPin)){digitalWrite(ledPin,HIGH);}}如果用前面提到的上拉電阻,就要改變一下boolean debounce的返回值如下:
boolean debounce(int pin){boolean state;boolean previousState;previousState=digitalRead(Pin);for(counter=0;counter<debounceDelay;counter++) {//增加debounceDelay值直到計數與按鍵動作同步delay(1);//等待1msstate=digitalRead(pin);//讀取引腳if(state!=previousState){counter=0;state=previousState;}}if(state==LOW)//low表示按下return true;elsereturn false; }//用count顯示按壓的次數 int count; void setup() {pinMode(inPin,INPUT)pinMode(outPin,OUTPUT) } void loop {if (debounce(input)){digitalWrite(outPin,HIGH);count++;Serial.println(count);} }/*
*switchTime 函數將返回開關被按壓的毫秒數
*使用上拉電阻
*靜態變量,即使在函數返回后仍然保留,只能在該函數內訪問,好處是不能被一些其他函數意外地修改
*/
const int switchAPin=2;//開關2 的引腳
const int switchBPin=3;//開關3 的引腳
//帶引用的函數必須顯式聲明
unsigned long switchTime(int pin, boolean &state,unsigned long &startTime);
long switchATime();
long switchBTime();
void setup()
{
pinMode(switchAPin,INPUT);
digitalWrite(switchAPin,HIGH);//打開上拉電阻
pinMode(switchBPin,INPUT);
digitalWrite(switchBPin,HIGH);//打開上拉電阻
}
void loop(){
unsigned long time;
Serial.print(",按鍵A的時間為:");
time=switchATime();
Serial.print(time);
Serial.print(",按鍵B的時間為:");
time=switchBTime();
Serial.print(time);
delay(1000);
}
unsigned long switchTime(int pin,boolean &state, unsigned long &startTime)
{
if(digitalRead(pin)!=state)//檢查開關量是否改變
{
state=!state;//是 則反轉狀態值
startTime=millis();//存儲時間
}
if(state == LOW)
return millis()-startTime;//返回以毫秒為單位的時間
else{return 0;//開關沒有按下 在HIGH狀態
}
}
long switchATime()
{
//聲明靜態變量
//開關狀態改變第一次檢測被檢測到的時間
static unsigned long startTime=0;
static boolean state;//開關的當前狀態
return switchTime(switchAPin,state,startTime);
}
long switchBTime()
{
//聲明靜態變量
//開關狀態改變第一次檢測被檢測到的時間
static unsigned long startTime=0;
static boolean state;//開關的當前狀態
return switchTime(switchBPin,state,startTime);
}
數字鍵盤
按鍵相當于一個常開開關
模擬輸入
**analogRead()**會自動設置輸入,
0V對應0,5V對應1023
/* 按一個電位器的位置設置的速度閃爍LED 注意區分模擬輸入0和數字輸入0
*/
const int potPin = 0; //選擇電位器的輸入引腳
const int ledPin = 13;
int val = 0;
void setup()
{
pinMode(ledPin, OUTPUT); //
}
void loop() {
val = analogRead(potPin); //讀取電位器上的電壓
digitalWrite(ledPin, HIGH);
delay(val);
digitalWrite(ledPin, LOW);
delay(val);
}
/* 按一個電位器的位置設置的速度閃爍LED
*/
const int potPin = 0; //選擇電位器的輸入引腳
const int ledPin = 13;
int val = 0;
void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
val = analogRead(potPin); //讀取電位器上的電壓
int percent;//映射的值
percent=map(val,0,1023,0,100);//map整數運算讀取的電壓值對應百分比 的映射
digitalWrite(ledPin, HIGH);
delay(percent);
digitalWrite(ledPin, LOW);
delay(100-percent);
Serial.println(percent);
}
*多路復用模擬輸入
測量電壓
應用:電量不足時LED閃爍,到臨界電壓級別時LED長亮
long warningThreshold = 1200; //毫伏在警告級別 - LED閃爍
long criticalThreshold = 1000; //臨界電壓級別 - LED長量
const int batteryPin = 0;
const int ledPin = 13;
void setup()
{
pinMode(ledPin, OUTPUT);
}
void loop()
{
int val = analogRead(batteryPin);
if (val < (warningThreshold * 1023L / 5000))
{ //在上面的行中 一個數后跟L得到一個32位的值
flash(val / 1023);
}
else if (val < (criticalThreshold * 1023L / 5000))
{
digitalWrite(ledPin, HIGH);
}
}
void flash(int percent)
{ digitalWrite(ledPin, HIGH);
delay(percent + 1);
digitalWrite(ledPin, LOW);
delay(100 - percent); //檢查延時==0?
}
d. 獲取傳感器輸?
傳感器提供信息的方式:
數字開關:傾斜 動作傳感器
模擬信號:光 振動 聲音 加速度
脈沖寬度:距離傳感器 pulseln命令測量脈沖持續時間
串口:RFID GPS
同步協議:I2C SPI
通用傳感設備 鼠標和游戲控制器等
各傳感器原理簡介見二、a
動作(數字量高低電平,類似開關)
//傾斜傳感器
const int tiltSensorPin = 2; //連接傳感器 的引腳
const int firstLEDPin = 11;
const int secondLEDPin = 12;
void setup() {
pinMode(tiltSensorPin, INPUT); //設置 傳感器引腳 為輸入
digitalWrite(tiltSensorPin, HIGH); //上拉電阻
pinMode(firstLEDPin, OUTPUT);
pinMode(secondLEDPin, OUTPUT);
}
void loop() {
if (digitalRead(tiltSensorPin) )
{
digitalWrite(firstLEDPin, HIGH);
digitalWrite(secondLEDPin, LOW);
}
else {
digitalWrite(firstLEDPin, LOW);
digitalWrite(secondLEDPin, HIGH);
}
}
可以理由按鍵消抖的原理消抖,程序略
動作檢測(集成被動紅外探測器PIR,只需判斷模擬量高電平)
這種傳感器引腳標有OUT - +
長這個樣子的傳感器基本上都是這么控制的↓,但是要注意信號電平的高低。
//紅外傳感器控制LED
const int inputPin = 2; //連接傳感器 的模擬輸入引腳2
const int ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT); //聲明LED作為輸出
pinMode(inputPin , INPUT); //被動紅外傳感器為輸入引腳
}
void loop() {
int val = digitalRead(inputPin);
if (val == HIGH)
{ digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);
}
}
光(光敏電阻模擬量)
//光敏電阻控制LED
const int SensorPin = 0; //連接傳感器 的模擬輸入引腳0
const int ledPin = 13;
void setup() {
pinMode(ledPin,OUTPUT);//LED引腳設為輸出
}
void loop() {
int rate=analogRead(sensorPin);//讀取模擬輸入
digitalWrite(ledPin,HIGH);//設置LED開
== delay(rate);//等待時間取決于光照水平==
digitalWrite(ledPin,LOW);//設置LED開
delay(rate);//等待時間取決于光照水平
}
距離(超聲波,輸出模式純凈高電平+輸入模式計算脈沖時間)
//超聲波距離傳感器控制LED
const int pingPin = 5; //連接傳感器 的模擬輸入引腳5
const int ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT); //聲明LED作為輸出
Serial.begin(9600);
}
void loop() {
int cm = ping(pingPin); Serial.println(cm);
digitalWrite(ledPin, HIGH);
delay(cm * 10); //每厘米增加了10ms的延遲
digitalWrite(ledPin, LOW);
delay(cm * 10);
}
int ping(int pingPin)
{ long duration, cm;
pinMode(pingPin, OUTPUT); //首先要給一個簡短的的低電平脈沖以確保高電位脈沖觸發
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
pinMode(pingPin, INPUT); //傳感器引腳改為輸入模式,接受信號
duration = pulseIn(pingPin, HIGH); //計算出傳感器引腳輸入從低到高的時間,單位為微秒
//時間轉換成距離
cm = duration * 29/ 2;//來回距離 超聲波速度29us/cm
}
有不需要ping的超聲波傳感器,也是用pulseIn(pingPin, HIGH),是計算脈沖寬度,直接讀值就可以。測量距離=pulseIn(pingPin, HIGH)*脈沖寬度速率
精確測距(*紅外IR傳感器比超聲波精度高但量程小,輸出非線性需要查表)
//紅外距離傳感器 測距輸出
const int sensorPin = 0; //連接傳感器 的模擬輸入引腳0
const int ledPin = 13;
const long referenceMv = 5000; //長整形以防止做乘法時溢出
void setup() {
pinMode(ledPin, OUTPUT); //聲明LED作為輸出
Serial.begin(9600);
}
void loop() {
int val = analogRead(sensorPin);
== int mv = (val * referenceMv) / 1023;==//mv為距離信號
Serial.print(mv);
Serial.print(",");
int cm = getDistance(mv);
Serial.println(cm);
digitalWrite(ledPin, HIGH);
delay(cm * 10); //每厘米增加了10ms的延遲
digitalWrite(ledPin, LOW);
delay(cm * 10);
}
const int TABLE_ENTRIES = 12;
const int firstElement = 250; //第一項是250mv
const int INTERVAL = 250; //每個項之間的毫伏數
static int distance[TABLE_ENTRIES] = {150, 140, 130, 100, 60, 50, 40, 35, 30, 25, 20,15};//器件手冊提供 假設單位是cm
int getDistance(int mV)
{
if (mV < INTERVAL )//判斷是否在范圍內, 可能還要寫個超出最大距離的判斷
return distance[TABLE_ENTRIES - 1]; //當距離比最小距離要小,返回最小距離
else
{
int index = mV / INTERVAL; //計算近似的信號值
float frac = (mV % 250) / (float)INTERVAL;//取余數,然后除以每項之差,用來插值計算
return distance[index] - ((distance[index] - distance[index + 1])* frac);
//返回計算出的距離值
}
}
振動(壓電傳感器/爆震傳感器,應力越大電壓越高。模擬量)
val=analogRead(sensorPin);
if(val>=THRESHOLD){…}
聲音
//駐極體話筒
const int sensorPin = 0; //連接傳感器 的模擬輸入引腳0(可以省略
const int ledPin = 13;
const int middleValue = 512; //模擬值范圍的中間值
const int numberOfSamples = 128; //每次讀取多少個數過小無法覆蓋完整波形周期,過大可能錯過短的聲音
int sample;//每次讀取的值
long signal;//你已經去除直流偏移之后的讀數
long averageReading;//循環讀數的平均值
long runningAverage = 0; //計算值的運行平均數
const int averagedOver = 16; //新值以何種程度影響運行平均值,數字越大速度越慢
const int threshold = 400; //在什么水平LED亮
void setup() {
pinMode(ledPin, OUTPUT); //聲明LED作為輸出
Serial.begin(9600);
}
void loop() {
long sumOfSquares = 0; //正負幅值的平方和
for (int i = 0; i < numberOfSamples; i++) {
sample = analogRead(0); //讀取一個數
signal = sample - middleValue; //算模擬信號 距離 直流中心值 的 偏移
signal *= signal;
sumOfSquares += signal;
}
averageReading = sumOfSquares / numberOfSamples; //計算運行平均值
runningAverage = ((averagedOver - 1) * runningAverage + averageReading) / averagedOver;
//即新運行平均值=原運行平均值+(這次采樣平均值-原運行平均值)/16,分母averagedOver越大,每次改變就越小
//新值影響平均值的速度就越慢
if (runningAverage > threshold) {
digitalWrite(ledPin, HIGH);
}
else {
digitalWrite(ledPin, LOW);
}
Serial.println(runningAverage);//打印檢查
}
const int TABLE_ENTRIES = 12;
const int firstElement = 250; //第一項是250mv
const int INTERVAL = 250; //每個項之間的毫伏數
static int distance[TABLE_ENTRIES] = {150, 140, 130, 100, 60, 50, 40, 35, 30, 25, 20, 15}; //器件手冊提供
int getDistance(int mV)
{
if (mV < INTERVAL * TABLE_ENTRIES - 1)
return distance[TABLE_ENTRIES - 1]; //當距離比最小距離要大,返回最小距離
else
{
int index = mV / INTERVAL; //插值計算,
float frac = (mV % 250) / (float)INTERVAL;
return distance[index] - ((distance[index] - distance[index + 1]) * frac);
}
}
溫度
類似光敏傳感器,該溫度傳感器模擬電壓與溫度成正比,需要寫相應程序計算。
*RFID標簽
RFID 射頻識別,對無線電頻率敏感,易被干擾。
硬件圖注意讀取器有使能和輸出引腳,前一個是arduino用來發給它激活信號(低電平)的,后一個是它把接收到的RFID傳輸給Arduino的,占用了硬串口,理論上你應該不能用電腦看到傳輸了什么數據。程序實現RFID讀取并對特定ID回應:
const int startByte = 10; //每個標簽之前的ASCII換行符
const int endByte = 13; //ASCII回車結束每個標簽
const int tagLength = 10; //標簽數位
const int totalLength = tagLength + 2; //標簽的長度 + 開始和結束字節
char tag[tagLength + 1]; //保存標簽和一個終止空白符
int bytesread = 0;
void setup() {
Serial.begin(2400);//設置為RFID閱讀器的波特率
pinMode(2, OUTPUT); //輸出到RFID
digitalWrite(2, LOW); //使能RFID}
}
void loop() {
if (Serial.available() >= totalLength) //檢測是否有足夠數據
{
if (Serial.read() == startByte)
{ bytesread == 0; //標簽的開始,重置計數為0
while (bytesread < tagLength) //讀取10位編碼
{ int val = Serial.read();
if ((val == startByte) || (val == endByte)) //檢查編碼結束
break;
tag[bytesread] = val;
bytesread = bytesread + 1; //準備讀取下一個數字
} if (Serial.read() == endByte) //檢查正確的結束字符
{ tag[bytesread] = 0; //終止字符串
Serial.print(“RFID 標簽是:”);
Serial.println(tag);
}
}
}
}
*旋轉動作(旋轉編碼器)
測量并顯示某物的旋轉,跟蹤速度和方向
代碼邏輯:
首先把AB對應的4、2引腳置為輸入模式,然后寫高激活,
當A腳的狀態和上次測量不同時,(上升沿或者下降沿)
B是低的,編碼器位置減1,
若B高電平,編碼器位置加1。
最后計算角度=(編碼器位置 %走一圈的步數)*360度/走一圈的步數
拓展知識:中斷
利用中斷可以實現只有在收到傳感器信號“當A腳的狀態和上次測量不同時,(上升沿或者下降沿)”的時候程序才會去檢查傳感器值,而不是始終檢查傳感器值。
*鼠標
*GPS
*加速度(陀螺儀測量角加速度,加速度計測量移動加速度如重力加速度)
e. 可視輸出、物理輸出、聲?輸出
顯示
1.數字輸出
pinMode(outputPin,OUTPUT)
digitalWrite(outputPin,value)
2.模擬輸出(uno的3 5 6 9 10 11引腳可以用)
analogWrite()
使用了一種脈沖寬度調制PWM技術,用數字脈沖模擬一個模擬信號
3.燈光控制。LED燈、陣列和數碼顯示,LCD文本和圖形顯示。
4.LED規格
5.復用,多個LED復用引腳,利用視覺暫留,靠按位掃描驅動。
利用復用技術16個引腳即可控制64個LED,
依次點亮所有LED的程序邏輯如下:
首先在setup中循環調好所有的行列引腳為輸出模式
然后在loop中,計數當前為第n個燈,/和%計算出行列值,
之后for循環掃描每一行列依次點燈,具體為:拉低當前列,要判斷當前點的燈有沒有達到n的位置,未達到或者剛好達到就輸出行高電平,超過就輸出低電平,最后延時為這個LED給出20ms的幀時間,然后給低電平,關燈。
6.最大引腳電流 <40mA <200mA。驅動高功率需要用晶體管和外接電源。
計算LED串聯的電阻值
數碼管
這個是共陽極,給低電平點亮 段
數碼管相當于8位LED串聯,分共陰極和共陽極
驅動單位共陽極數碼管的程序邏輯:
首先定義數組存儲字形碼
const byte numeral[10]={
//ABCDEF/dp 共陰極的字形碼
B11111 1100,//0
, , , , …};2 個字節的每一位可以代表一個引腳的輸出狀態
然后定義引腳,設置為輸出模式
在循環中調用數碼管顯示函數顯示值,延時
數碼管顯示的函數:void showDigit(int number),循環檢查是否點亮 for(int segement=1;segment<8;sengment++) 用到bitRead(numeral[number],segment) 讀給定數字的字形碼,賦值給isBitSet,如果為1,則isBitSet != isBitSet ,如果是共陰極數碼管就不用這個操作,最后對應引腳拉低。
驅動多位LED數碼管顯示器
在單位的基礎上,增加一個位數循環,依次點亮指定位指定的led段
為解決引腳占用,可以接MAX7221等數碼管驅動芯片,需要查芯片的命令手冊,并用到SPI通信,
用自定義函數sendCommend(int command, int value)發送指令函數,利用digitalWrite(使能引腳,LOW) SPI.transfer(command);SPI.transfer(value); digitalWrite(使能引腳,HIGH)
電機
1.舵機 即直流伺服電機。分為帶位置反饋的角度控制舵機和斷開位置反饋的連續旋轉舵機。
紅正棕負橙信號
控制舵機角度:
第一步庫和對象
#include <Servo.h>//引入舵機庫
Servo myservo;//創建舵機對象來控制指定舵機
第二步 信號引腳連接對象
void setup()
{
myservo.attach(9);//把連接在引腳9上的舵機賦予舵機對象
}
第三步使用wite寫角度
void loop()
{
myservo.write(angle);
}
正反轉搖擺:
#include <Servo.h>//引入舵機庫 Servo myservo;//創建舵機對象來控制舵機int angle = 0; //用來存儲舵機位置的變量void setup() {myservo.attach(9);//把連接在引腳9上的舵機賦予舵機對象 }void loop() {for (angle = 0; angle < 180; angle++){ //步長為1度myservo.write(angle);delay(20);//舵機命令之間等待20ms}for (angle = 180; angle > 0; angle--){myservo.wirte(angle);//在相反方向上移動舵機delay(20);}}如果是連續旋轉舵機,控制同理,但是給90°停止轉動,距離90°越遠,向一個方向轉速越快。
2.有刷電機和無刷電機
可用H橋電路控制有刷電機,直接用元件即可,需要接一個用來使能的模擬引腳用analogWrite()PWM控制速度,一對數字引腳控制不同方向旋轉和停止。
具體略。
可利用業余調速器控制無刷電機 代碼和舵機代碼相等
3.步進 每步走1°、2°、30°或更多。也是H橋,但可以用Stepper的庫
4.振動馬達 像點亮LED一樣就行,代碼見第"三"部分
音頻
揚聲器原理
壓電裝置:施加脈沖時會產生聲音的陶瓷換能器
有源蜂鳴器自身包含震蕩源,給高電平就能響,
所以介紹無源蜂鳴器,由arduino給音頻信號
如果要同時產生多個音調做出電子琴的效果,可以用Tone.h的庫,用switch分支選擇輸出給揚聲器的音調,演奏對象.play(音調)方法。
f. ?線通信和?絡
* I2C和SPI
I2C內部集成電路和SPI串行外設接口標準的建立是為傳感器和微控制器之間的數字信息傳輸提供簡單的方法。
I2C只需要2路信號連接到arduino,同一時間只能在一個方向上傳送。通常用于不需要大量數據的傳感器。(加速度計、外部實時時鐘RTC、外部存儲器芯片EPROM、數字溫度計、LED數碼管、另一個arduino等)
SPI速度更快,有獨立的輸入輸出連線,可以同時收發數據。
I2C總線的兩路連線叫SCL和SDA,Arduino作為主設備。
用Wire.h庫可以輕松初始化和通訊。
下面一個例子是和游戲手柄I2C通訊
時鐘
無線XBEE
分為發送端和接收端,用到VirturalWire.h 庫傳輸文本消息
藍牙
//藍牙模塊 //電腦串口發送消息arduino收到后發到藍牙端,藍牙端發消息,arduino收到后發給電腦串口 //硬件串口的引腳0 1要和電腦發消息占用了,所以用軟串口傳輸信息 #include<SoftwareSerial.h>const int rxpin = 2; //引腳用于接收 const int txpin = 3; //引腳用來發送SoftwareSerial buletooth(rxpin, txpin); //給出的端口上的新串口void setup() {Serial.begin(9600);bluetooth.begin(9600);//初始化軟件串口Serial.println("Serial ready");bluetooth.println("藍牙準備就緒"); }void loop() {if (bluetooth.available()) {char c = (char)bluetooth.read();Serial.write(c);}if (Serial.available()) {char c = (char)Serial.read();bluetooth.write(c);} }*互聯網
1.以太網:低級別的信號層,提供基本的物理信息傳遞能力。這些信息的源地址和目的地址由媒界訪問控制MAC地址來確定。arduino程序定義的MAC地址需要在網絡上唯一。
2.傳輸控制協議TCP和網際協議IP,
3.本地IP地址,路由器提供,在路由器上的動態主機配置協議DHCP服務創建。從web瀏覽器發出的web請求和所得到的的回應使用超文本傳輸協議HTTP信息。網頁通常使用超文本標記語言HTML格式。網絡交換格式已被開發用在應用計算機軟件實現可靠的網絡數據提取,XML和JSON是流行的格式。
三. 上述技術在設計原型制作和開發中的應?。
1.接收到____信號時,_____動作
最簡單的可以看做按鈕和LED燈
按鈕可以換成各種傳感器還有通訊傳輸接受的數據,LED可以換成電機、蜂鳴器、顯示屏等。
原理圖都和這個大差不差,圖片順時針轉90度,
左邊模擬量
連傳感器、H橋驅動模塊等元件的使能引腳給速度值
右邊數字量
連開關、LED、有源蜂鳴器、馬達、傳感器的使能腳、H橋的方向引腳。
PWM(3/5/6/9/10/11):電機、無源蜂鳴器等。
數字引腳通過程序可以模擬成串口通信引腳。
*(0和1一般不用,占用硬件串口UART。
2和3可以接外部中斷。10-13SPI通信)
細化改一下可以做智能窗簾。
2.智能水杯(傳感器、顯示器、LED、蜂鳴器、藍牙等等綜合運用)
溫度傳感器(塑料水杯) 壓力傳感器(水量監控) LCD液晶顯示器 LED燈x2 蜂鳴器
水量監控:壓力傳感器實時健康水量。
智能飲水累計:初始飲水量為0,如果壓力傳感器計數小于1,暫停一段時間再計數,并計算飲水數。如果一段時間后飲水量未變,蜂鳴器報警。
不同溫度下指示燈不同,溫度傳感器監測到水溫超過。
水溫報警功能,如果水溫過高拿起水杯,蜂鳴器響起,提示使用者。
藍牙用于信息傳輸,可以控制硬件。
總結
以上是生活随笔為你收集整理的Arduino基础知识复习12.18的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: while 和 do while
- 下一篇: 启动hadoop组件时报错:(Permi