---
# System prepended metadata

title: <center>2. 蜂鳴器_數位類比轉換器</center>
tags: [物聯網]

---

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

``` 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)
```
![](https://i.imgur.com/aoW8aon.png)
* 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

![](https://i.imgur.com/pKCiXaB.png)

* 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 (從設備).

![](https://i.imgur.com/r0JiwbA.png)
* 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 必需保持訊號穩定，不可以改變，以⽅便對⽅讀取資料。綠⾊區域。
![](https://i.imgur.com/wthKWhT.png)
## I²C的傳輸步驟
* 使⽤I²C時，數據被轉換成messages，messages則被分解為data frames。每條messages都有⼀個address frame，其中包含從裝置(slave)的⼆進位地址，以及⼀個或多個包含正在傳輸的數據的data frame。
Messages還包括每個data frame之間的start和stop條件，read/write和ACK / NACK bit.

![](https://i.imgur.com/FIkUWNk.png)

* 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

![](https://i.imgur.com/3KRfJTr.png)
![](https://i.imgur.com/zgYaJo4.png)

* 發送數據

![](https://i.imgur.com/8EPkZOE.png)

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

![](https://i.imgur.com/WEulndw.png)

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

![](https://i.imgur.com/qQlcjYg.png)
## 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階

![](https://i.imgur.com/AWCyKYT.png)
## AD/DA 轉換PCF 8591傳感器模組

![](https://i.imgur.com/SjgERKG.png)
![](https://i.imgur.com/1LLdrkJ.png)

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

## 查看I²C裝置的位置

```
sudo i2cdetect -y 1
```

![](https://i.imgur.com/QQ8TEs1.png)

## 對應裝置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）

![](https://i.imgur.com/vIL89EI.png)

```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: `物聯網`