# <center>2. 蜂鳴器_數位類比轉換器</center>
<p style="text-align:right">Steven Lee</p>
## 蜂鳴器
* 蜂鳴器基本上分為兩種:有源蜂鳴器和無源蜂鳴器.
* 這裡的“源”不是指電源。⽽是指震盪源。
* 有源蜂鳴器
* 有源蜂鳴器內部帶震盪源,所以只要⼀通電就會發出聲響, 輸入不同的⽅波頻率時, 也會產⽣不同的聲響.
* 無源蜂鳴器
* 有稱為Passive被動式, 類似於喇叭, 因為內部沒有震盪來源, 所以⼀定要輸入⽅波才有聲⾳.
* active buzzer
* "-" : GND
* "S" : GPIO
* 中間的VCC: 5V


``` python=
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(19, GPIO.OUT)
while True:
select = int(input("Please select the type of output <1-3>, 0:exit -> "))
if select == 0:
GPIO.output(19, 0)
break
if select == 1:
for i in range(1000):
for j in range(250):
GPIO.output(19,1)
for j in range(250):
GPIO.output(19,0)
if select == 2:
for i in range(1000):
for j in range(250):
GPIO.output(19,1)
for j in range(1000):
GPIO.output(19,0)
if select == 3:
for i in range(1000):
for j in range(1000):
GPIO.output(19,1)
for j in range(250):
GPIO.output(19,0)
```

* passive buzzer
* "-" : GND
* "S" : GPIO
* 中間的VCC: 5V
``` python=
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(21, GPIO.OUT)
while True:
select = int(input("Please select the type of output <1-3>, 0:exit -> "))
if select == 0:
GPIO.output(21, 0)
break
if select == 1:
for i in range(1000):
for j in range(250):
GPIO.output(21,1)
for j in range(250):
GPIO.output(21,0)
if select == 2:
for i in range(1000):
for j in range(250):
GPIO.output(21,1)
for j in range(1000):
GPIO.output(21,0)
if select == 3:
for i in range(1000):
for j in range(1000):
GPIO.output(21,1)
for j in range(250):
GPIO.output(21,0)
```
## 思考⼀下: 比較聲⾳有何差異?
## 思考⼀下: 如何控制⾳量⼤⼩?
## 讓電⼦元件唱歌
* https://zh.wikipedia.org/wiki/%E9%9F%B3%E7%AC%A6

* Let's sing a song
``` python=
import RPi.GPIO as GPIO
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(21, GPIO.OUT)
GPIO.output(21, 1)
p1 = GPIO.PWM(21, 10)
p1.start(0) # dc is 0
# 1155665 0 4433221 0 5544332 0 5544332 0 1155665 0 4433221
sheets = [262,262,392,392,440,440,392,0,
349,349,330,330,294,294,262,0,
392,392,349,349,330,330,294,0,
392,392,349,349,330,330,294,0,
262,262,392,392,440,440,392,0,
349,349,330,330,294,294,262]
for s in sheets:
if s == 0:
p1.ChangeDutyCycle(0)
else:
p1.ChangeFrequency(s)
p1.ChangeDutyCycle(50)
time.sleep(0.2)
p1.ChangeDutyCycle(0)
time.sleep(0.1)
p1.stop()
```
## 請同學實作其他旋律
## I2C
* I²C(Inter-Integrated Circuit)為I²C Bus簡稱,所以中文應該叫積體電路匯流排,它是⼀種串列通訊匯流排,使⽤多主從架構,由⾶利浦公司在1980年代為了讓主機板、嵌入式系統或⼿機⽤以連接低速週邊裝置⽽發展。I²C的正確讀法為「I平⽅C」("I-squared-C"),⽽("I-two-C")則是另⼀種錯誤但被廣泛使⽤的讀法。
* I²C的參考設計使⽤⼀個7位元⻑度的位址空間但保留了16個位址,所以在⼀組匯流排最多可和112個節點通訊。常⾒的I²C匯流排依傳輸速率的不同⽽有不同的模式:標準模式(100 kbit/s)、低速模式(10 kbit/s),但時脈頻率可被允許下降⾄零,這代表可以暫停通訊。⽽新⼀代的I²C匯流排可以和更多的節點(⽀援10位元⻑度的位址空間)以更快的速率通訊:快速模式(400 kbit/s)、⾼速模式(3.4 Mbit/s)超⾼速模式(5Mbit/s)。
* I2C Bus 架構及介⾯接腳, 它是以匯流排型式介接,同時匯流排上允許有多個 master (主設備) 和多個slave (從設備).

* I2C 的匯流排接線⼗分簡單,只有兩條訊號線:
* 資料線 (SDA,Serial Data Line)
* 時脈線 (SCL,Serial Clock Line)
* I2C 的 I/O 接腳不管是 master 或 slave的這⼆⽀接腳都是 CMOS Open Drain 或者是 TTL Open Collector
* 並不輸出某⼀特定電壓值或電流值, 輸出的電壓必需由外部決定.
* I2C Bus 為 Wired-AND(AND 閘的邏輯狀態當全部輸入是 High 時,輸出為 High ; 只要任何⼀個輸入為Low 時,輸出即為 Low)。所以當全部的晶片輸出為High時,接線可測到High;但只要有⼀晶片輸出為Low是接線則會測到Low。
## I2C Bus 資料傳輸
* Start : SCL 為 High 時,SDA 由 High 轉為 Low。S黃⾊區域
* Stop: SCL 為 High 時,SDA 由 Low 轉為 High。P黃⾊區域
* SCL 為 Low 時,SDA 可以改變資料。藍⾊區域。
* SCL 為 High 時,SDA 必需保持訊號穩定,不可以改變,以⽅便對⽅讀取資料。綠⾊區域。

## I²C的傳輸步驟
* 使⽤I²C時,數據被轉換成messages,messages則被分解為data frames。每條messages都有⼀個address frame,其中包含從裝置(slave)的⼆進位地址,以及⼀個或多個包含正在傳輸的數據的data frame。
Messages還包括每個data frame之間的start和stop條件,read/write和ACK / NACK bit.

* start condition: SCL: 從High切換到Low 之前, SDA: 從High切換到Low
* stop condition: SCL: 從Low切換到High 之前, SDA: 從Low切換到High
* read/write bit: 主裝置(master)向slave 發送data(low), 從slave請求data)(high)
* ACK/NACK bit: 回應訊號
* I²C沒有slave選擇線, 因此只有⼀個⽅法能讓slave知道data正在發送給他, 透過message中的第⼀個address frame來得知.
* master將想要與slave設備的地址發送給每個連接的slave設備. 然後每個slave設備從master設備發送的address與⾃⼰比較. 如果相同, 則將Low ACK訊號發回master主機. 若不相同則不執⾏任何操作, SDA維持在high


* 發送數據

* 傳輸data frame後, slave device回送ACK (OW), 給Master確認

* 多個I²C設備連接⽅式

## AD/DA 轉換器
* 數位訊號
* 離散時間, 不連續的訊號
* 類比訊號 (⾃然界現象)
* 連續時間, 連續訊號
* A/D 轉換器(Aanlog-to-Digitial Converter, ADC), 將類比訊號轉換成數位訊號的裝置.
* D/C 轉換器(Digital-to-Analog Converter, DAC), 將數位訊號轉換成類比訊號的裝置.
* 轉換的階數 (Or ADC resolution)
* 3 bit = 8 階
* 4 bit = 16階
* 8 bit = 256階
* 10 bit = 1024階
* 12 bit = 4096階

## AD/DA 轉換PCF 8591傳感器模組


* 4個analog的模擬輸入
* 1個analog的模擬輸出
* 1個I²C2的介⾯
* ⼯作電壓範圍: 2.5V - 6V
* AD採樣為8-bit
* A0: 電位計調節輸入電壓
* A1: 光敏電阻
* A2: 熱敏電阻
* A3: analog的模擬輸入
## 查看I²C裝置的位置
```
sudo i2cdetect -y 1
```

## 對應裝置analog 輸入接腳的位置
```
A0 = 0x40
A1 = 0x41
A2 = 0x42
A3 = 0x43
```
## 控制 PCF 8591上的LED 燈透過A0
``` python=
import smbus
import time
address = 0x48
cmd = 0x40
value = 0
bus = smbus.SMBus(1) #使⽤/dev/i2c-1, 接線在pin 3, 5
while True:
bus.write_byte_data(address, cmd, value)
value += 1
if value == 256:
value = 0
print(f"red led analog value is {value}")
time.sleep(0.01)
```
## 透過PCF 8591上的電位計調節輸入電壓控制AO
``` python=
import smbus
import time
import math
address = 0x48
A0 = 0x40
A1 = 0x41
A2 = 0x42
A3 = 0x43
bus = smbus.SMBus(1)
while True:
bus.write_byte(address,A0)
bus.read_byte(address)
value = bus.read_byte(address)
bus.write_byte_data(address, A0, value)
print(f"Current value is {value}")
time.sleep(1)
```
## 讀取 PCF 8591上的 光敏電阻, 透過A1
* 愈暗值愈⼤
```python=
import smbus
import time
import math
address = 0x48
A0 = 0x40
A1 = 0x41
A2 = 0x42
A3 = 0x43
bus = smbus.SMBus(1)
while True:
bus.write_byte(address,A1)
bus.read_byte(address)
value = bus.read_byte(address)
print(f"Current value is {value}")
time.sleep(1)
```
## 熱敏電阻公式
* NTC THERMISTOR (NEGATIVE TEMPERATURE COEFFICIENT THERMISTOR), 溫度上升電阻值下降
* NTC 熱敏電阻溫度計算公式:Rt = R EXP(B(1/T1-1/T2))
* T1和T2指的是K度, K:開爾文溫度
* 絕對零度 = 0K,等於攝⽒溫標零下273.15度(即−273.15℃)
* Rt 是熱敏電阻在T1溫度下的阻值。
* R是熱敏電阻在T2常溫下的標稱阻值。
* 100K的熱敏電阻25℃的值為100K(即R=100K)
* T2=(273.15+25)
* B值是熱敏電阻的引數
* T1=1/(ln(Rt/R)/B+1/T2)

```python=
import smbus
import time
import math
address = 0x48
A0 = 0x40
A1 = 0x41
A2 = 0x42
A3 = 0x43
bus = smbus.SMBus(1)
vin = 3.3
R = 10000
B = 3950.0
T2 = 273.15+25.0
while True:
bus.write_byte(address,A2)
bus.read_byte(address)
value = bus.read_byte(address)
v = vin*(value/255)
rt = R*(v/(vin-v))
t1 = 1/((math.log(rt/R)/B)+(1/T2))
t = int((t1 - 273.15)*100)/100
print(value)
print(f"Current temperature is {t}")
time.sleep(1)
```
## 讀取 PCF 8591上的Analog輸入, 透過A3
* A0類比輸出腳位,可以接到PCF 8591上的Analog輸入腳位,得到0~1023的數值。⽽D0數位輸出腳位,則是依Threshold Value(⾨檻值),來決定輸出LOW或HIGH,⽽「⾨檻值」則可以由板⼦上的可變電阻來
調整:順時針,⾨檻值提⾼;逆時針,⾨檻值降低;調整時可以看綠燈有沒有亮。
```python=
import smbus
import time
address = 0x48
A0 = 0x40
A1 = 0x41
A2 = 0x42
A3 = 0x43
bus = smbus.SMBus(1)
while True:
bus.write_byte(address,A3)
bus.read_byte(address)
value = bus.read_byte(address)
print(value)
print(f"Current value is {value}")
time.sleep(0.1)
```
###### tags: `物聯網`