2021年10月16日 星期六

建構 Micro Python 的工作環境

(一)下載、安裝 Python 的最新版本

網址:https://www.python.org/

可以看到網站上現在可以下載的最新版本是:Python 3.10.0 版,下載、安裝完成後,我們需要確認是否安裝成功,在 Windows 系統請開啟命令提示字元後,輸入:

C:\python -V
Python 3.10.0

若版本訊息顯示如上面黃字一樣 3.10.0,那就代表安裝成功。若你的系統是 MAC,則請輸入

tmps07@> python3 -V
Python 3.10.0

(二)下載 MicroPython 韌體

網址:https://micropython.org/download/

找到自己要運行的控制板,如我使用的是 WeMos D1 Mini,所以我選擇 ESP8266:

進入後我們可以選取最新的版本 esp8266-20210902-v1.17.bin,如下圖:

(三)下載 ESP8266 韌體燒錄程式:esptool

esptool 是使用 Python 語言編寫成的 ESP8266 韌體燒入程式,我們可以用它來把上面下載的韌體 esp8266-20210902-v1.17.bin 寫到 ESP8266 的晶片,esptool 的下載指令為:

tmps07@> sudo pip3 install esptool

pip 會自動搜尋、下載並安裝指定的套件,同時也會依據套件的相依性安裝額外需要的套件,從下載的過程中可以看到,除了 esptool 以外,pip 還連帶下載了其他 3 個套件。

pip 套件管理員還具備列表(list)、更新(upgrade)、刪除(uninstall) 等功能。

列表
tmps07@> pip3 list
更新:更新套件
tmps07@> sudo pip3 install --upgrade esptool
更新:pip
tmps07@> python3 -m pip install --upgrade pip
刪除:刪除套件
tmps07@> sudo pip3 uninstall esptool

(四)安裝 USB 驅動程式

CH340 晶片:CH34x Serial
CP2102 晶片:CP210x Serial

驅動程式安裝後,將控制板使用 USB 連接線連接,在 Windows 系統可以透過以下步驟來檢查驅動程式是否有正確安裝:

Windows 圖示按右鍵 -> 裝置管理員 -> 連接埠 (COM 和 LPT)

在 MAC 系統則以指令來檢查:

tmps07@> ls -l /dev/tty.*

(五)利用 esptool 將韌體寫入晶片

要將下載的韌體寫入 ESP8266 晶片前,宣要先將晶片內目前的程式清除,指令為:

tmps07@> esptool.py --port /dev/tty.subserial1420 erase_flash

其中 /dev/tty.subserial1420 是控制板與電腦的連接埠。

清除晶片的程式後,就可以將韌體寫入晶片了:

tmps07@> esptool.py --port /dev/tty.subserial1420 --baud 460800 write_flash --flash_size=detect 0 esp8266-20210902-v1.17.bin

(六)使用 Screen 終端機來操作 MicroPython 控制板

tmps07@> screen /dev/tty.subserial1420 115200

MicroPython 快速鍵:

Up ArrowDown Arrow:瀏覽之前輸入過的指令。
Ctrl+A:進入原始模式(Raw)。
Ctrl+B:離開原始模式。
Ctrl+C:中斷程式執行。
Ctrl+E:進入貼入模式。
Ctrl+D:在非貼入模式為「暖開機,將控制板重新啟動」。
在貼入模式時 Ctrl+C -> Ctrl+D:離開貼入模式。
Ctrl+A -> K -> Y:結束 screen,離開 MicroPython 的 REPL 模式。

(七)下載安裝 ampy 控制板管理程式

tmps07@> pip3 install adafruit-ampy

日後若需要更新 ampy,則以指令:

tmps07@> pip3 install adafruit-ampy --upgrade

來更新。

(七)ampy 控制板管理程式的操作

在我們利用 esptool 將韌體寫入 ESP8266 控制板後,除了可以使用 screen 指令來即時操作 MicroPython 控制板,我們還可以利用其他的文字編輯器(例如:vsCode)先把我們的 MicroPython 程式編完後,再利用 ampy 上傳到 MicroPython 控制板執行。

只執行 led.py 不上傳:

tmps07@> ampy --port /dev/tty.subserial-1410 run --no-output led.py

把 led.py 上傳到控制板:

tmps07@> ampy --port /dev/tty.subserial-1410 put led.py

列出控制板上目前有哪些程式:

tmps07@> ampy --port /dev/tty.subserial-1410 ls

把控制板上 led.py 下載到電腦:

tmps07@> ampy --port /dev/tty.subserial-1410 get led.py

刪除控制板上 led.py 程式:

tmps07@> ampy --port /dev/tty.subserial-1410 rm led.py

在控制板上建立資料夾 dd:

tmps07@> ampy --port /dev/tty.subserial-1410 mkdir dd

刪除控制板上的資料夾 dd:

tmps07@> ampy --port /dev/tty.subserial-1410 rmdir dd

2021年8月1日 星期日

Ubuntu 實用的快捷鍵

1、Super 鍵:打開活動搜索界面

使用 Super 鍵可以打開活動菜單。如果你只能在 Ubuntu 上使用一個快捷鍵,那只能是 Super 鍵。

想要打開一個應用程式?按下 Super 鍵然後搜索應用程式。如果搜索的應用程式未安裝,它會推薦來自應用中心的應用程式。

想要看看有哪些正在運行的程序?按下 Super 鍵,屏幕上就會顯示所有正在運行的 GUI 應用程式。

想要使用工作區嗎?只需按下 Super 鍵,就可以在屏幕右側看到工作區選項。

注意
Linux 中的 Super 鍵即鍵盤上帶有 Windows 圖標的鍵,這裏我們使用了大寫字母,但這不代表你需要按下 shift 鍵,比如,T 代表鍵盤上的 『t』 鍵,而不代表 Shift+t。

2、 Ctrl+Alt+T:打開 Ubuntu 終端窗機

想要打開一個新的終端,只需使用快捷鍵 Ctrl+Alt+T。這是我在 Ubuntu 中最喜歡的鍵盤快捷鍵。

3、 Super+L 或 Ctrl+Alt+L:鎖屏

當我們離開電腦時鎖定屏幕,是最基本的安全習慣之一。我們可以使用 Super+L 快捷鍵,而不是繁瑣地點擊屏幕右上角然後選擇鎖定屏幕選項。有些系統也會使用 Ctrl+Alt+L 鍵鎖定屏幕。

4、 Super+D or Ctrl+Alt+D:顯示桌面

我們可以通過單擊屏幕左下角的 9 個點打開 Ubuntu 18.04 GNOME 中的應用程式菜單。 但是一個更快捷的方法是使用 Super+A 快捷鍵。

它將顯示應用程式菜單,可以在其中查看或搜索系統上已安裝的應用程式。另外,我們可以使用 Esc 鍵退出應用程式菜單界面。

6、 Super+Tab 或 Alt+Tab:在運行中的應用程式間切換

如果在運行的應用程式不止一個,則可以使用 Super+Tab 或 Alt+Tab 快捷鍵在應用程式之間切換。

按住 Super 鍵同時按下 Tab 鍵,即可顯示應用程式切換器。 按住 Super 的同時,繼續按下 Tab 鍵在應用程式之間進行選擇。當光標在所需的應用程式上時,鬆開 Super 和 Tab 鍵。

默認情況下,應用程式切換器從左向右移動。 如果要從右向左移動,可使用 Super+Shift+Tab 快捷鍵。

在這裡也可以用 Alt 鍵代替 Super 鍵。

提示
如果有多個應用程式實例,可以使用 Super+` 快捷鍵在這些實例之間切換。

7、 Super+箭頭:移動視窗位置

這個快捷鍵也適用於 Windows 系統。 使用應用程式時,按下 Super+左箭頭,應用程式將貼合屏幕的左邊緣,占用屏幕的左半邊。

同樣,按下 Super+右箭頭會使應用程式貼合右邊緣。

按下 Super+上箭頭將最大化應用程式窗口,Super+下箭頭將使應用程式恢復到其正常的大小。

8、 Super+M:切換到通知欄

GNOME 中有一個通知欄,我們可以在其中查看系統和應用程式活動的通知,這裡也有一個日曆。

使用 Super+M 快捷鍵,可以打開此通知欄。 如果再次按這些鍵,將關閉打開的通知托盤。

使用 Super+V 也可實現相同的功能。

9、 Super+空格:切換輸入法(用於多語言設置)

如果使用多種語言,可能系統上安裝了多個輸入法。 例如,我需要在 Ubuntu 上同時使用 印地語 和英語,所以我安裝了印地語(梵文)輸入法以及默認的英語輸入法。

如果您也使用多語言設置,則可以使用 Super+空格 快捷鍵快速更改輸入法。

10、 Alt+F2:運行控制台

這適用於高級用戶。 如果要運行快速命令,而不是打開終端並在其中運行命令,則可以使用 Alt+F2 運行控制台。

當使用只能在終端運行的應用程式時,這尤其有用。

11、 Ctrl+Q:關閉應用程式窗口

如果有正在運行的應用程式,可以使用 Ctrl+Q 快捷鍵關閉應用程式窗口。我們也可以使用 Ctrl+W 來實現此目的。

Alt+F4 是關閉應用程式窗口更「通用」的快捷方式。但它不適用於一些應用程式,如 Ubuntu 中的默認終端。

12、 Ctrl+Alt+箭頭:切換工作區

如果是使用工作區的重度用戶,可以使用 Ctrl+Alt+上箭頭和 Ctrl+Alt+下箭頭在工作區之間切換。

13、 Ctrl+Alt+Del:登出

在 Linux 中使用著名的快捷鍵 Ctrl+Alt+Del 並不會像在 Windows 中一樣打開任務管理器,而是會打開登出對話方塊。

在普通的 GNOME 桌面環境中,我們可以使用 Ctrl+Alt+Del 鍵打開關機菜單,但 Ubuntu 並不總是遵循此規範,因此當我們在 Ubuntu 中使用 Ctrl+Alt+Del 鍵時,它會打開註銷菜單。

在 Ubuntu 中使用自定義鍵盤快捷鍵

我們不是只能使用默認的鍵盤快捷鍵,可以根據需要創建自己的自定義鍵盤快捷鍵。

轉到「設置->設備->鍵盤」,將在這裡看到系統的所有鍵盤快捷鍵。向下滾動到底部,就可以看到「自定義快捷方式」選項。

自訂快捷鍵時,我們需要提供易於識別的快捷鍵名稱、使用快捷鍵時運行的命令,以及自定義的按鍵組合。

2021年7月26日 星期一

dd - 備份與回覆資料的小工具

Linux 與 freeBSD 系統中的 dd 指令是一個多功能的小工具,可以用於各種的資料拷貝動作:

  • 備份與回復整顆硬碟的資料。
  • 備份與回復原始設備檔案,例如 MBR(master boot record)。
  • 轉換資料格式,例如 ASCII 轉換為 EBCDIC,大小寫轉換等。
  • 建立固定大小的檔案。

dd 的原意為 data duplicator,但由於 dd 屬於較低階的資料處理工具,通常都會以管理者(root)權限來執行,如果稍有不慎,也很容易造成嚴重的後果(例如整顆硬碟的資料不見等等),所以有些人也把 dd 取名為 data destroyer。許多人在製作 Linux 的 USB 安裝隨身碟的時候,時常也會使用到 dd 指令。

基本使用方式

  • if=FILE:指定輸入檔案名稱(input file)為 FILE。
  • of=FILE:指定輸初檔案名稱(output file)為 FILE。
  • ibs=BYTES:指定輸入區塊大小(input block size),一次讀取 BYTES 位元組的資料,預設為 512
          位元組。
  • obs=BYTES:指定輸出區塊大小(output block size),一次寫入 BYTES 位元組的資料,預設為
          512 位元組。
  • bs=BYTES:指定 block size,一次讀取與寫入 BYTES 位元組的資料,此選項會覆蓋 ibs 與 obs 的
          設定。
  • cbs=BYTES:一次轉換 BYTES 位元組的資料。
  • count=N:只處理 N 個輸入區塊,每個區塊的大小為 ibs。
  • seek=N:在輸出時跳過輸出檔案的前 N 個區塊,每個區塊的大小為 obs。
  • skip=N:在輸入時跳過輸入檔案的前 N 個區塊,每個區塊的大小為 ibs。
  • conv=CONVS:指定資料的轉換選項,如果一次要指定多種轉換,則以逗點分隔。

以下是各種可用的轉換:

  • ascii:EBCDIC 轉 ASCII。
  • ebcdic:ASCII 轉 EBCDIC。
  • ibm:ASCII 轉 alternate EBCDIC。
  • block:將每一個區塊的資料結尾的換行字元替換為空白,並以空白將整個區塊補足 cbs 位元組。
  • unblock:將每個區塊結尾的空白字元替換為換行字元。
  • lcase:將大寫字母轉換成小寫。
  • ucase:將小寫字母轉換成大寫。
  • swab:將每一對輸入的位元組交換。
  • sync:將每一個輸入的區塊以 NUL 補足至 ibs 位元組的大小,如果是在 block 或是 unblock 的轉換
          中,則以空白字元來補足。
  • nocreat:不要建立輸出檔案。
  • notrunc:不要將輸出檔案截短。
  • noerror:發生錯誤時還是繼續執行。
  • fdatasync:讓資料同步實體寫入硬碟,不要留在緩衝區中。
再次提醒您,若以管理者權限執行 dd 時,請再三確認您所執行的指令內容是否正確,執行錯誤的指令可能導致整個系統與資料的損毀!

備份整顆硬碟

將 /dev/sda 所有的資料寫入 /dev/sdb:

sudo dd if=/dev/sda of=/dev/sdb

if 參數指定的是輸入檔案(input file),而 of 參數指定的是輸出檔案(output file),這行指令會將 /dev/sda 這顆硬碟的資料讀出來,然後寫進 /dev/sdb 這顆硬碟。

在整個過程中如果出現讀取錯誤的話,dd 就會停止執行,如果想要讓 dd 在出現讀取錯誤時還是繼續拷貝資料的話,就要加上 conv=noerror 參數,這個選項通常在備份資料時會使用到,另外加上 sync 可以讓 dd 以 synchronized I/O 的方式備份資料:

sudo dd if=/dev/sda of=/dev/sdb conv=noerror,sync

建立硬碟的備份影像檔

備份硬碟資料除了拿兩顆硬碟對拷之外,也可以直接將整顆硬碟的資料製作成影像檔,以這種方式備份資料會更有彈性:

sudo dd if=/dev/hda of=~/hdadisk.img

這行指令會將 /dev/sda 這顆硬碟的資料讀出來,儲存至 ~/hdadisk.img 這個影像檔中。

從備份影像檔回復硬碟資料

如果要以影像檔回復硬碟的資料,就執行:

sudo dd if=hdadisk.img of=/dev/hdb

這樣就會把 hdadisk.img 影像檔中的資料回復至 /dev/sdb 這顆硬碟(原本 /dev/sdb 硬碟中的所有資料會被覆蓋掉,請小心使用)。

備份磁碟分割區

如果只要備份單一個磁碟分割區,可以使用:

sudo dd if=/dev/hda1 of=~/partition1.img

兩個磁碟分割區對拷:

sudo dd if=/dev/sda1 of=/dev/sdb1 bs=4096 conv=noerror,sync

這樣會將 /dev/sda1 的資料同步至 /dev/sdb1,使用前請確認 /dev/sdb1 分割區大小至少要比 /dev/sda1 大。

備份 CD/DVD 光碟

將光碟片放進光碟機之後,就可以使用這樣的指令將光碟整個備份成 ISO 檔:

sudo dd if=/dev/cdrom of=tgsservice.iso bs=2048

如果您放進光碟片時,系統會自動掛載,那麼在使用 dd 備份之前,建議可以先將光碟卸載,避免不必要的光碟讀取動作。

壓縮資料

如果資料很大,可以配合 gzip 將資料壓縮起來,直接輸出成壓縮過的影像檔:

sudo dd if=/dev/sda | gzip > sdadisk.img.gz

而要從壓縮過的影像檔回復資料,可以使用:

sudo gzip -dc sdadisk.img.gz | dd of=/dev/sda

MBR 的備份與回復

如果資料很大,可以配合 gzip 將資料壓縮起來,直接輸出成壓縮過的影像檔:

sudo dd if=/dev/sda | gzip > sdadisk.img.gz

而要從壓縮過的影像檔回復資料,可以使用:

sudo gzip -dc sdadisk.img.gz | dd of=/dev/sda

MBR 的備份與回復

備份 MBR(master boot record):

sudo dd if=/dev/sda of=mbr.img bs=446 count=1

回復 MBR:

sudo dd if=mbr.img of=/dev/sda

查看 MBR 內容:

sudo dd if=/dev/hda of=mbr.bin bs=512 count=1
od -xa mbr.bin

ASCII 與 EBCDIC 轉換

將 ASCII 轉換為 EBCDIC:

dd if=textfile.ascii of=textfile.ebcdic conv=ebcdic

將 EBCDIC 轉換為 ASCII:

dd if=textfile.ebcdic of=textfile.ascii conv=ascii

大小寫轉換

轉換為大寫:

dd if=file1 of=file2 conv=ucase

轉換為小寫:

dd if=file1 of=file2 conv=lcase

建立固定大小的檔案

建立一個 10MB 大小的檔案:

dd if=/dev/zero of=file1 bs=10485760 count=1

這裏的 block size 的計算方式為 10*1024*1024 = 10MB。

修改檔案內容

將檔案開頭的 512 bytes 改為 null:

dd if=/dev/zero of=file1 bs=512 count=1 conv=notrunc

這裡的 notrunc 參數代表不要將輸出檔案截短,只取代開頭的前 512 bytes,其餘內容不變。假設 file1 不存在,那麼這行指令就會建立一個 512 bytes 的 file1 檔案。

使用 dd 指令將 Linux ISO 檔製作成 Live USB 隨身碟

這裡介紹如何在 Linux 中將一般 Linux 的 ISO 檔用 dd 指令寫入 USB 隨身碟,製作成 Live USB 或是安裝系統用的隨身碟。

大部分的 Linux 發行版都會以 ISO 映像檔的形式提供使用者下載,以前大家都會將 ISO 映像檔用燒錄成 CD 或 DVD 安裝光碟,再放進機器的光碟機中安裝系統,不過現在光碟片已經逐漸被 USB 隨身碟所取代了,有些新電腦甚至已經不再配有光碟機,以 USB 隨身碟的方式來安裝系統已經成為主流。

如果手上沒有可用的 USB 隨身碟,也可以拿 SD 或 MicroSD 等記憶卡,接上讀卡機之後也可以作為安裝 Linux 用的儲存媒體,其使用方式跟一般的 USB 隨身碟一模一樣。

STEP 1

將 USB 隨身碟插入 Linux 的電腦中,查詢一下目前所有硬碟與 USB 隨身碟的狀況。

lsblk

NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda           8:0    1  7.6G  0 disk 
└─sda1        8:1    1  7.6G  0 part /media/pi/ESD-USB
sdc           8:33   1  7.4G  0 disk 
└─sdc1        8:33   1  7.4G  0 part /media/pi/disk
mmcblk0     179:0    0 14.9G  0 disk 
├─mmcblk0p1 179:1    0   63M  0 part /boot
└─mmcblk0p2 179:2    0 14.8G  0 part /

一般來說在 USB 隨身碟插入 Linux 系統之後,系統會自動將其掛載,我們必須先從這個列表中找出我們要使用的 USB 隨身碟是那一個,最簡單的方式就是從這裡的 SIZE 來看,若看不出來的話,就打開其掛載的目錄,實際看一下裡面的內容。

這裡我所要使用的 USB 隨身碟是掛載在 /media/pi/disk 這一個位置,而其對應的硬碟路徑則是 /dev/sdc,找到這個代號之後,就可以繼續下一步了。

在判斷磁碟代號的時候,要非常小心,絕對不可以搞錯,如果誤判磁碟代號的話,在執行後續的資料寫入動作時,就會可能造成整個系統損毀。

STEP 2

從 Linux 的桌面環境下卸載 USB 隨身碟(在檔案總管按下退出的按鈕),或是使用指令卸載:

umount /media/pi/disk

卸載後再次確認一下卸載的 USB 隨身碟是否正確:

lsblk

STEP 3

使用 dd 指令將 Linux 的 ISO 映像檔寫入 USB 隨身碟:

sudo dd if=ubuntu-16.10-desktop-amd64.iso of=/dev/sdc bs=1M

這個指令是將整個 ISO 檔的內容寫入 USB 隨身碟,所以要等比較久一點,在寫入的期間並不會有任何輸出訊息,完成後會出現類似這樣的訊息:

/dev/sdc bs=1M
1520+0 records in
1520+0 records out
1593835520 bytes (1.6 GB) copied, 493.732 s, 3.2 MB/s

這樣就完成了 USB 隨身碟的製作了,接著就可以將隨身碟拔下來,插入要安裝 Linux 系統的機器進行安裝了。

How to write FreeBSD image to USB disk for installation using dd command

FreeBSD is an open-source and free Unix-like operating system descended from the Berkeley Software Distribution (BSD). This quick tutorial explains how to download and prepare the USB stick/pen boot drive to install FreeBSD 12.2 or 13.x to install FreeBSD using the target computer’s USB port.

Head over to the FreeBSD download page and make sure you download the latest stable bootable version. I will download *-memstick.img*, which contains all of the files needed to install FreeBSD, its source, and the Ports Collection using a USB stick and boot the system.

STEP 1 - Downloading FreeBSD image

use the wget command you can download a file with curl too:

wget
https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/12.2/FreeBSD-12.2-RELEASE-amd64-memstick.img.xz

Session :

--2020-12-30 00:46:08--  https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/12.2/FreeBSD-12.2-RELEASE-amd64-memstick.img.xz
Resolving download.freebsd.org (download.freebsd.org)... 203.80.16.151, 2404:a8:3ff::15:0
Connecting to download.freebsd.org (download.freebsd.org)|203.80.16.151|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 687961180 (656M) [application/octet-stream]
Saving to: 'FreeBSD-12.2-RELEASE-amd64-memstick.img.xz'
             
FreeBSD-12.2-RELEAS 100%[==================>] 656.09M  8.26MB/s    in 81s     
             
2020-12-30 00:47:29 (8.09 MB/s) - 'FreeBSD-12.2-RELEASE-amd64-memstick.img.xz' saved [687961180/687961180]

Grab checksum file to verify boot disk image :

wget
https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/12.2/CHECKSUM.SHA512-FreeBSD-12.2-RELEASE-amd64

Verify integrity of a file :

sha512sum --ignore-missing -c CHECKSUM.SHA512-FreeBSD-12.2-RELEASE-amd64"

We should see something as follows :

FreeBSD-12.2-RELEASE-amd64-memstick.img.xz: OK

For FreeBSD 13.x :

Download FreeBSD 13 by visiting official page:

$ wget https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/13.0/FreeBSD-13.0-RELEASE-amd64-memstick.img.xz
$ wget https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/13.0/CHECKSUM.SHA512-FreeBSD-13.0-RELEASE-amd64
$ sha512sum --ignore-missing -c CHECKSUM.SHA512-FreeBSD-13.0-RELEASE-amd64
FreeBSD-13.0-RELEASE-amd64-memstick.img.xz: OK
STEP 2 - Finding out your USB flash drive name

Insert the USB pen into your computer’s USB port and run the dmesg to see USB device name :

dmesg

The output indicates that I am using /dev/sda on Linux :

[ 2314.466297] usb 1-2: new high-speed USB device number 9 using xhci_hcd
[ 2314.629772] usb 1-2: New USB device found, idVendor=0951, idProduct=1666, bcdDevice= 0.01
[ 2314.629779] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 2314.629782] usb 1-2: Product: DataTraveler 3.0
[ 2314.629786] usb 1-2: Manufacturer: Kingston
[ 2314.629788] usb 1-2: SerialNumber: C81F660E262EF1713606D2C2
[ 2314.655320] usb-storage 1-2:1.0: USB Mass Storage device detected
[ 2314.655377] scsi host0: usb-storage 1-2:1.0
[ 2314.655474] usbcore: registered new interface driver usb-storage
[ 2314.656962] usbcore: registered new interface driver uas
[ 2315.663917] scsi 0:0:0:0: Direct-Access     Kingston DataTraveler 3.0      PQ: 0 ANSI: 6
[ 2315.664256] sd 0:0:0:0: Attached scsi generic sg0 type 0
[ 2315.665411] sd 0:0:0:0: [sda] 30218842 512-byte logical blocks: (15.5 GB/14.4 GiB)
[ 2315.665723] sd 0:0:0:0: [sda] Write Protect is off
[ 2315.665725] sd 0:0:0:0: [sda] Mode Sense: 4f 00 00 00
[ 2315.665939] sd 0:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[ 2315.682431] sda: sda1 sda2
[ 2315.684945] sd 0:0:0:0: [sda] Attached SCSI removable disk
[ 2316.154272] ISO 9660 Extensions: Microsoft Joliet Level 3
[ 2316.155734] ISO 9660 Extensions: Microsoft Joliet Level 3
[ 2316.159081] ISO 9660 Extensions: RRIP_1991A

Your device name will vary. For instance, here is what reported on my FreeBSD system (/dev/da0) :

ugen0.3: <SanDisk Ultra> at usbus0
umass0 on uhub2
umass0: <SanDisk Ultra, class 0/0, rev 2.10/1.00, addr 2> on usbus0
umass0: SCSI over Bulk-Only; quirks = 0xc100
umass0:8:0: Attached to scbus8
da0 at umass-sim0 bus 0 scbus8 target 0 lun 0
da0: <SanDisk Ultra 1.00> Removable Direct Access SPC-4 SCSI device
da0: Serial Number 4C531003510422116045
da0: 40.000MB/s transfers
da0: 59232MB (121307136 512 byte sectors)
da0: quirks=0x2<lNO_6_BYTE>

Make sure you extract .xz file as follows :

xz -dv FreeBSD-12.2-RELEASE-amd64-memstick.img.xz

For FreeBSD 13.x image :

xz -dv FreeBSD-13.0-RELEASE-amd64-memstick.img.xz

FreeBSD-13.0-RELEASE-amd64-memstick.img.xz (1/1)
100 %     661.7 MiB / 1,040.4 MiB = 0.636   173 MiB/s       0:05
Step 3 - Writing FreeBSD image to USB flash drive

Warning:
Be careful with the correct USB pen device/drive name. The wrong device name will destroy the existing data on the specified USB drive.

Say, If your USB stick is /dev/sda under Linux, you would run :

# Find mounted partition for /dev/sda #
$ mount | grep '/dev/sda'
# Unmount all mounted partition for /dev/sda #
$ sudo umount /dev/sda2
# Write it #
$ sudo dd if=FreeBSD-12.2-RELEASE-amd64-memstick.img \
of=/dev/sda bs=1M conv=sync status=progress

For FreeBSD 13.x :

sudo dd if=FreeBSD-13.0-RELEASE-amd64-memstick.img \
of=/dev/sda bs=1M conv=sync status=progress

If your USB flash stick name is /dev/da0 under FreeBSD, you would run :

# Find mounted partition for /dev/da0 #
$ mount | grep '/dev/da0'
# Unmount all mounted partition for /dev/da0 #
$ sudo umount /dev/da0
# Write it #
$ sudo dd if=FreeBSD-12.2-RELEASE-amd64-memstick.img \
of=/dev/da0 bs=1M conv=sync
Step 4 - Installing FreeBSD 12.2

Insert the newly created USB pen drive with FreeBSD installer in the target computer. Please turn it on. Make sure you boot from USB media. For example :

Once booted, you will see the FreeBSD boot menu followed by FreeBSD installer :

Now follow the on-screen instructions to install the FreeBSD operating system.

2021年7月21日 星期三

ESP32 的第一課

ESP32 晶片內部有兩個處理器:「32 位元雙核心處理器」和「8 位元低功耗處理器(簡稱:ULP)」。其中 ESP32 主處理器的兩個核心分別叫做 PRO_CPUAPP_CPU

  • PRO_CPU:代表 Protocol CPU,負責處理和操控 WiFi、藍芽和其他介面(I2C、SPI...等)。
  • APP_CPU:代表 Application CPU,執行主程式碼。

規格比較

型號ESP32ESP8266
處理器Xtensa 雙核心或單核心 32 位元 LX6Xtensa 單核心 32 位元 L106
工作時脈160/240 MHz80 MHz
協同處理器ULP
SRAM 主記憶體520 KB120 KB
外部 Flash 快閃記憶體最高支援 16MB最高支援 16 MB
RTC(即時鐘)記憶體16 KB
WiFi2.4 GHz 頻段的 802.11 b/g/n2.4 GHz 頻段的 802.11 b/g/n
藍芽4.2 版 BR/EDR/BLE
乙太網路10/100 Mbps
CAN 介面 2.0
GPIO3416
UART 序列埠31.5(其中一組僅有傳送腳)
I2C 介面21
I2S 介面22
SPI 介面42
ADC(類比轉數位)18 個(12 位元),最大輸入電壓 3.6V1 個(10 位元),最大輸入電壓 1.1V
DAC(數位轉類比)2個(8 位元)2個(8 位元)
PWM 輸出168
觸控介面10
溫度感測器有,用於測量晶片溫度
霍爾感測器有,用於檢測磁力變化
安全性安全啟動 Flash 加密 1024 OTP
加密(crypto)AES、SHA-2、RSA、ECC、ENG
功耗深度睡眠 10 uA深度睡眠 2 uA
註①:CAN 是 Controller Area Network(控制器區域網路)通訊協定的簡稱,基本用於工廠、汽車、雜
   訊較多的場所。

ESP32 圖例

NodeMCU-32S

NodeMCU-32S Pins MAP

ESP32的功能接腳與限制

  • Serial
    硬體接腳 UART 0:TX(GPIO1)、RX(GPIO3)
    硬體接腳 UART 1:TX(GPIO9)、RX(GPIO10)
    硬體接腳 UART 2:TX(GPIO17)、RX(GPIO16)
    軟體模擬接腳:事實上,ESP32 軟體模擬序列埠,可以使用在任意接腳,其宣告語法為「Seria2.begin(9600,SERIAL_8N1,18,19)」,其中:
       Serial2:類別變數名
       9600:鮑率
       SERIAL_8N1:代表沒有同位檢查
       18:GPIO18 為 RX
       19:GPIO19 為 TX
  • I2C
    SDA:GPIO22
    SCL:GPIO21
  • SPI
    CLK:GPIO14
    MISO:GPIO12
    MOSI:GPIO13
    SS:GPIO15
  • VSPI
    SCK:GPIO18
    MISO:GPIO19
    MOSI:GPIO23
    SS:GPIO5
  • 接腳的限制
    (1)接腳的最大輸入電壓為 3.6V
    (2)不支援上拉電阻(INPUT_PULLUP)的接腳:32、35、34、39、36
    (3)不支援下拉電阻(INPUT_PULLDOWN)的接腳:0、1、2、3
    (4)接腳 34、35、36、39 只支援輸入
    (5)接腳 0、5、14、15 不建議設為 INPUT
    (6)接腳 1、3、12 不建議設為 OUTPUT
    (7)接腳 CLK、SD0、SD1、SD3、CMD 用於上傳程式至晶片,不能使用
    (8)接腳定額的電流為 6mA,最大電流為 12mA
註②:ESP32 的 GPIO 接腳 6、7、8、9、10、11 都連接到快閃記憶體,所以 Serial1 的
   RX(GPIO9)、TX(GPIO10)無法使用程式控制,是專門用來與記憶體溝通的接腳

ESP32 的 printf() 格式化輸出

雖然 Arduino 不支援 printf(),但基於 ESP-IDF 工具的 ESP32 跟 C 語言一樣,可以支援這個萬用的格式化輸出工具。

符號說明
%c單一字元(char)
%s字串(string)
%u無正負號(unsigned)整數
%d整數(integer)
%lu無正負長整數(unsigned long)
%ld長整數(long)
%x16 進位(hexadecimal)整數
%f浮點數(float),輸出全部整數部分,6 位小數部分,超過 6 位則四捨五入
%.nf輸出小數點後 n 位數字的浮點數,請留意 n 前面有個點「.」。例如:3.14159 用 %.3f 輸出為 3.142
%%顯示 %

數位輸出/輸入

接觸新的開發板時,最先要留意的是它的工作電壓規格

  • ESP32 開發版的工作電壓(VDD)是 3.3V。
  • 數位及類比腳的最大容許輸入電壓是 VDD+0.3V,也就是 3.6V。
  • pinMode(pin,mode);

    mode 可能的值:

    • INPUT:當接腳設成「數位輸入」時,部分接腳會因以下的事件而不建議使用。
      (1) TX(GPIO1)連接 USB 埠,開機時會輸出除錯訊息。
      (3) RX(GPIO1)連接 USB 埠,開機時會處於高電位狀態。
      (12) GPIO1 開機時須維持在低電為,否則不能開機。
    • OUTPUT:當接腳設成「數位輸出」時,部分接腳會因以下的事件而不建議使用。
      (1) GPIO0、GPIO5、GPIO14、GPIO15 開機時會輸出短暫 PWM 訊號,不建議使用。
      (1) GPIO1、GPIO3 連接 USB 埠。
      (1) GPIO34、GPIO35、GPIO36、GPIO39 僅具備數位輸入功能。
    • INPUT_PULLUP:GPIO32~GPIO39 不具備上拉電阻之功能。
    • INPUT_PULLDOWN:GPIO0~GPIO3 不具備下拉電阻之功能。

    讀取電容觸控接腳

    ESP32 有 10 個接腳具備電容觸控感應功能,可以當除觸控開關,觸控腳的感測值是類比值,會因接線形式(觸控面積大小)、距離等因素而不同。讀取觸控感測值的語法為:

    int touchValue(pin);

    可使用的觸控接腳如下表:
    T0(GPIO4)T1(GPIO0)T2(GPIO2)T3(GPIO5)T4(GPIO13)
    T5(GPIO12)T6(GPIO14)T7(GPIO27)T8(GPIO33)T9(GPIO32)

    範例:底下程式將在使用者碰接腳 T3(GPIO15)時,會點亮開發版內建的 LED。

    #include LED LED_BUILTIN
    const byte touchPin = 15;
    const int thresHold = 20;
    int touchValue ;
    void setup()
    {
        pinMode(LED_BUILTIN, OUTPUT);
    }
    
    void loop()
    {
        touchValue = touchRead(touchPin);
    
        if(touchValue < thresHold)
        {
            digitalWrite(LED, LOW);
        }
        else
        {
            digitalWrite(LED, HIGH);
        }
        delay(1000);
    }

    程式的執行效果:

    ESP32 的類比輸入

    ESP32 類比輸入電壓的上限是 3.6V,正常最高值為 3.3V,讀取類比輸入值的指令同樣是 analogRead(),接腳可用 A0(GPIO36)、A3(GPIO39)、A4(GPIO32)、A5(GPIO33)、A6(GPIO34)、A7(GPIO35) 等編號。

    Arduino UNO 的類比數位轉換器(ADC)採 10 位元取藥,所以量化值介於 0~1023,ESP32 的 ADC 預設採 12 位元取樣,量化值介於 0~4095。

    設定 ESP32 的 ADC 類比輸入電壓範圍與取樣位元的方法:

    • analogSetAttenuation(衰減值):其中衰減值可能為
    • ADC_0db:0db 衰減,電壓輸入上限為 1.00V。
    • ADC_2_5db:2.5db 衰減,電壓輸入上限為 1.34V。
    • ADC_6db:6db 衰減,電壓輸入上限為 2.00V。
    • ADC_11db:11db 衰減,電壓輸入上限為 3.6V。
    • analogSetWidth(寬度):設定 ADC 的取樣位元
    • analogSetWidth(9):取樣寬度為 0~511。
    • analogSetWidth(10):取樣寬度為 0~1023。
    • analogSetWidth(12):取樣寬度為 0~4095。
    • analogRead(pin)::可用的類比輸入接腳為
    • A0:GPIO36,只有輸入功能。
    • A3:GPIO39,只有輸入功能。
    • A6:GPIO34,只有輸入功能。
    • A7:GPIO35,只有輸入功能。
    • A4:GPIO32。
    • A5:GPIO33。

    由於 ESP32 類比輸入的最高電壓是 3.6V,但如果類比感測器的輸出電壓是 5V,直接將感測器 5V 的輸出接到 ESP32 的類比輸入接腳,很可能會造成 ESP32 的損壞,所以我們需要將 5V 轉為 3V3。

    透過右圖的接線方式,我們可以順利達成這個目標。

    ESP32 內建的霍爾感應器

    ESP32 晶片內建一個可以偵測磁場變化的「霍爾效應」感測器,霍爾感測器的輸出電壓和磁場的強度與距離成正比。

    霍爾感測器普遍應用在檢測物體移動、馬達轉速、開闔等。例如:無刷馬達的轉子包覆著磁石,在馬達內部裝設霍爾感測器,透過偵測磁石南北極的變化便可以測量馬達的轉速。在手機、平板電腦內部裝設霍爾感測器,同時在保護蓋說這一個磁石,便可以達成偵測保護蓋開闔的動作。

    註③:霍爾效應(Hall effect)是指當固體導體放置在一個磁場內,且有電流通過時,導體內的電荷載子
       會因受力而偏向一邊,繼而產生電壓。

    讀取霍爾感測值的函式叫做 hallRead(),它將傳回帶正負號的整數值。

    ESP32 的類比輸出

    ESP32 的 PWM 解析度不是固定值,而是跟頻率設定息息相關,根據樂鑫公司的 ESP32 官方技術文件(http://bit.ly/39wGPa5)指出:PWM 解析度隨著頻率提高而降低,解析度範圍介於 1~16 位元,頻率上限為 40MHz,解析度的計算公式請參考下圖。

    設定 ESP32 的 PWM 輸出指令

    設置 ESP32 的 PWM 輸出、頻率,須透過兩個函式來達成,韓式名字開頭的 ledc 代表 LED Control(LED 控制,簡稱 LEDC)。

    • ledcSetup(通道,頻率,解析度):設置 PWM 產生器
    • ledcAttachPin(接腳編號,通道)
      通道相當於 PWM 產生器,ESP32 內部共有 16 個 PWM 通道(0~15),每個通道的輸出都可設定給任一個數位輸出接腳。所以 ESP32 最多同時擁有 16 個 PWM 輸出腳。PWM 設定完畢,就可以執行 ledcWrite() 輸出 PWM 訊號。
    • ledcWrite(通道,PWM 輸出):參閱以下範例
      ledcSetup(0,5000,10); //--- 為通道 0 設定一個 5KHz、10 位元取樣頻率的 PWM 產生器
      ledcAttachPin(5,10); //--- 把 GPIO5 指定給通道 0 使用
      ledcWrite(0,614); //--- 在通道 0 輸出 614 的 PWM 訊號

    利用 PWM 輸出完成調光器

    實驗電路:利用一個 10K 的便電阻連接在 A4 接腳,接線示範圖如下

    程式碼如下:

    #define BITS 10
    
    void setup()
    {
        pinMode(LED_BUILTIN,OUTPUT);
        analogSetAttenuation(ADC_11db);
        analogSetWidth(BITS);
    
        ledcSetup(0,5000,BITS);
        ledcAttachPin(LED_BUILTIN,0);
    }
    
    void loop()
    {
        int val=1023-analogRead(A4);
        ledcWrite(0,val);
    }

    調控 PWM 訊號的頻率發出聲音

    ESP32 不支援 Arduino 的 tone() 函式,但 Arduino 的 tone() 本質上是用 PWM 來調控輸出波形(聲音)的頻率,所以 ESP32 也同樣可以用 PWM 訊號來驅動發音元件。透過改變訊號的「工作週期」來調整輸出電壓,以達成在揚聲器上發出不同音高的聲音。

    在 ESP32 上改變 PWM 頻率的函式語法:

    • ledcWriteTone(通道,頻率);
    • ledcWriteNote(通道,音名常數,音階編號);

    音名、音階編號與頻率的對應表:

    音名音名常數音階編號
    012345678
    CNOTE_C163365131262523104620934186
    C#NOTE_Cs173569139277554110922174435
    DNOTE_D183773147294587117523494699
    D#NOTE_Eb193978156311622124524894978
    ENOTE_E214182165330659131926375274
    FNOTE_F224487175349698139727945588
    F#NOTE_Fs234693185370740148029605920
    GNOTE_G254998196392784156831366272
    G#NOTE_Gs2652104208415831166133226645
    ANOTE_A2855110220440880176035207040
    A#NOTE_Bb2958117233466932186437297459
    BNOTE_Bb3162123247493988197639517902

    下面的兩行程式碼都可以在通道 0 產生中央 C 音:

    ledcWriteTone(0,262); //--- 在通道 0 產生 262Hz 的頻率
    ledcWriteNote(0,NOTE_C,4)

    另,Arduino 有個停止音頻輸出的 noTone(); 函式,在 ESP32 中可用底下這行敘述達成:

    ledcWriteTone(0,0); //--- 把通道 0 的頻率設為 0

    其他 LEDC 控制函式:

    • ledcRead(通道):傳回指定通道的工作週期(佔空比)值。
    • ledcReadFreq(通道):傳回指定通道的頻率值(若佔空比為 0 則傳回 0)。
    • ledcDetachPin(接腳):解除指定接腳的 LEDC 功能,即取消 PWM 輸出。

    最後,我們用一段程式來展示控制 ESP32 讓蜂鳴器發出聲音救護車警示音,接線法請參閱下圖。

    #define BITS 10
    #define BUZZER 22 //--- 蜂鳴器接腳
    
    void setup()
    {
        ledcSetup(0,2000,BITS); //--- PWM 設定為:通道 0、頻率 20KHz、解析度 10 位元
        ledcAttachPin(BUZZER,0);
    }
    
    void loop()
    {
        ledcWriteNote(0,NOTE_E,5);
        delay(500);
        ledcWriteNote(0,NOTE_A,4);
        delay(500);
    }

    2021年6月12日 星期六

    Arduino 讀取/寫入 SD 卡資料

    隨著接觸越來越多的感測器,在 Arduino 紀錄感測元件接收的數值的同時,把數值儲存下來已經是非常重要的需求。除了透過網路模組送出以外,Arduino 開發板本身並不像 Raspberry Pi 有外接 SD 或 Micro SD 卡的介面,可以儲存在記憶卡上,要寫入資料到 SD卡上,就必須透過 SPI 的介面,才能將感測的資料寫入 SD卡。

    接線圖
    Arduino Uno×1、SD 卡模組×1、連接線×6
    註:DI 代表 SD卡模組 MOSI,DO代表 SD卡模組 MISO
    SD 卡模組連接線路

    SD 卡模組Arduino
    GNDGND
    +5V5V
    CSPin 4
    MOSIPin 11
    SCK(CLK)Pin 13
    MISOPin 12

    SPI匯流排規定了 4 個保留邏輯訊號介面:(維基百科)

    • SCLK (Serial Clock):串行時鐘,由主機發出
    • MOSI (Master Output,Slave Input):主機輸出從機輸入訊號,由主機發出
    • MISO (Master Input,Slave Output):主機輸入從機輸出訊號,由從機發出
    • SS (Slave Selected) 或 Chip Select (CS):由主機發出,低電位有效
    SD Library

    SD Class

    • 初始設定 SD卡及函式庫:SD.begin(cspin)
    • cspin (選項):Arduino 連接SD卡模組 SS 或 CS的 Pin腳
    • 檢查括號內的檔案名稱是否存在:exists()
    • 建立目錄:SD.mkdir(filename)
    • 開啟檔案:SD.open(filepath, mode)
    • mode:檔案開啟模式
      FILE_READ 開啟唯讀檔案,從檔案開始處讀取
      FILE_WRITE 開啟讀寫檔案,從檔案最後處開始寫入
    • 移除檔案:SD.remove(filename)
    • 移除目錄:SD.rmdir(filename)

    File Class

    • 關閉檔案:file.close()
    • 將 data 字串寫到檔案中,不跳行:file.print(data)
    • 將 data 字串寫到檔案中,不跳行:file.println(data)
    • 將 data 字串寫入檔案中:file.write(data)
    程式碼
    #include <SPI.h>
    #include <SD.h>
    //--- #include <SdFat.h>
    //--- SD.h 與 SdFat.h 為不同 Library 所提供,使用時擇一使用
    
    File myFile;
    //--- SdFat SD;
    //--- 必須與 SdFat.h 搭配使用
    
    //--- 設定 SD library 功能變數:
    
    Sd2Card card;
    SdVolume volume;
    SdFile root;
    
    const int chipSelect = 4;
    
    void setup()
    {
      Serial.begin(9600); //--- 開啟通訊串列埠開啟
      while (!Serial) {} //--- 等待串列埠連線
    
      //--- 寫入檔案
      Serial.print("\nWaiting for SD card ready...");
    
      if (!SD.begin(4))
      {
        Serial.println("Fail!");
        return;
      }
      kSerial.println("Success!");
    
      //--- 開啟檔案,一次僅能開啟一個檔案
      myFile = SD.open("card.txt", FILE_WRITE
    
      if (myFile)
      { //--- 檔案開啟正常
        Serial.print("Write to card.txt...");
        myFile.println("Test to write data to SD card..."); //--- 繼續寫在檔案後面
        myFile.close(); //--- 關閉檔案
        Serial.println("Completed!");
      }
      else
      { // 無法開啟時顯示錯誤
        Serial.println("\n open file error");
      }
    
      //--- 顯示 SD 卡資訊
    
      if (!card.init(SPI_HALF_SPEED, chipSelect))
      {
        Serial.println("initialization failed. Check: SD Card");
        return;
      }
      else
      {
        Serial.println("============= Card Information ==================");
      }
    
      //--- 顯示 SD 卡類型
    
      Serial.print("Card type: ");
      switch (card.type())
      {
        case SD_CARD_TYPE_SD1:
          Serial.println("SD1");
          break;
        case SD_CARD_TYPE_SD2:
          Serial.println("SD2");
          break;
        case SD_CARD_TYPE_SDHC:
          Serial.println("SDHC");
          break;
        default:
          Serial.println("Unknow");
      }
    
      //--- Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
      if (!volume.init(card))
      {
        Serial.println("Could not find FAT16/FAT32 partition.");
        return;
      }
    
      //--- 顯示類型和 FAT 空間大小
      uint32_t volumesize;
      Serial.print("Volume type is FAT");
      Serial.println(volume.fatType(), DEC);
      Serial.println();
    
      volumesize = volume.blocksPerCluster(); // clusters are collections of blocks
      volumesize *= volume.clusterCount();    // we'll have a lot of clusters
      volumesize *= 512;                      // SD card blocks are always 512 bytes
    
      Serial.print("Volume size (bytes): ");
      Serial.println(volumesize);
      Serial.print("Volume size (Kbytes): ");
      volumesize /= 1024;
      Serial.println(volumesize);
      Serial.print("Volume size (Mbytes): ");
      volumesize /= 1024;
      Serial.println(volumesize);
            
      Serial.println("\nFiles found on the card (name, date and size in bytes): ");
      root.openRoot(volume);
            
      //--- list all files in the card with date and size
      root.ls(LS_R | LS_DATE | LS_SIZE);
            
      Serial.println("================= Finished =====================");
    }
    
    void loop() {} //--- 在Setup()執行完成後,就等結束
    SD Library Documents
    • SD.h Library

      The SD library allows for reading from and writing to SD cards, e.g. on the Arduino Ethernet Shield. It is built on sdfatlib by William Greiman. The library supports FAT16 and FAT32 file systems on standard SD cards and SDHC cards. It uses short 8.3 names for files. The file names passed to the SD library functions can include paths separated by forward-slashes, /, e.g. "directory/filename.txt". Because the working directory is always the root of the SD card, a name refers to the same file whether or not it includes a leading slash (e.g. "/file.txt" is equivalent to "file.txt"). As of version 1.0, the library supports opening multiple files.

      The communication between the microcontroller and the SD card uses SPI, which takes place on digital pins 11, 12, and 13 (on most Arduino boards) or 50, 51, and 52 (Arduino Mega). Additionally, another pin must be used to select the SD card. This can be the hardware SS pin - pin 10 (on most Arduino boards) or pin 53 (on the Mega) - or another pin specified in the call to SD.begin(). Note that even if you don't use the hardware SS pin, it must be left as an output or the SD library won't work.

    • SdFat.h Librry

      The Arduino SdFat Library is a minimal implementation of FAT16 and FAT32 file systems on SD flash memory cards. Standard SD and high capacity SDHC cards are supported.

      1. The SdFat only supports short 8.3 names.
      2. The main classes in SdFat are Sd2Card, SdVolume, and SdFile.

      The Sd2Card class supports access to standard SD cards and SDHC cards. Most applications will only need to call the Sd2Card::init() member function.

      The SdVolume class supports FAT16 and FAT32 partitions. Most applications will only need to call the SdVolume::init() member function.

      The SdFile class provides file access functions such as open(), read(), remove(), write(), close() and sync(). This class supports access to the root directory and subdirectories. A number of example are provided in the SdFat/examples folder. These were developed to test SdFat and illustrate its use. SdFat was developed for high speed data recording. SdFat was used to implement an audio record/play class, WaveRP, for the Adafruit Wave Shield. This application uses special Sd2Card calls to write to contiguous files in raw mode. These functions reduce write latency so that audio can be recorded with the small amount of RAM in the Arduino.

    • Arduino SD記憶卡模組之讀與寫

    再論 - 微型物聯網 (IoT) 架構 - MQTT

    IoT 最重要的莫過於網路通訊了,機器與機器 ( M2M ) 間需要透過 HTTP 來傳輸資料,了解比較深入的人,可能還知道 CoAP 或 MQTT 等。

    MQTT 是一種物聯網的通訊協定,最初是由 IBM 和 Eurotech 主導開發,並已在 2014 年正式成為了 OASIS 國際標準,開發的目的是為了在窄寬帶以及低耗能條件下,傳送與接收處理訊息,採用 Publish/Subscribe 的方式,透過 Broker 做訊息溝通。

    MQTT 的標頭 ( header ),僅佔 2 個 bytes ,縮小了傳輸量。整體來說,資料封包也比要傳送同等訊息的 HTTP 來的小,這種特性讓 MQTT 非常適合運用在現行的 IoT 架構中。

    Publisher、Subscriber、Broker 又是什麼?

    Publish / Subscribe 為一種訊息規範的模式 :

    發布者:Publisher,不會將訊息直接傳送給訂閱者:Subscriber,而是將訊息分為不同的主題:Topic,訂閱者只接收已訂閱的主題。

    MQTT 即是採用 Publish/Subscribe 模式,其中 Publisher 和 Subscriber 都是用戶端(Client),Broker是伺服器端(Server) 負責轉發 Topic。

    Publish / Subscribe 示意圖

    Subscriber 告知 Broker 想要訂閱的 Topic,每當 Publisher 發布訊息時,Broker 會依照 Topic,傳送給訂閱的 Subscriber。

    由於 PublisherSubscriber 之間有 Broker 當作中繼站,所以兩邊並不需要知道彼此的 IP。

    IBM Watson IoT Platform from https://www.ibm.com/developerworks/
    Topic 主題格式

    MQTT 的 Topic 是字串 ( String ),並支援階層式命名如下範例:

    outside/temperature/temp_Device01
    inside/humidity/humid_Device07
    Taiwan/Taipei/Datong/ChengdeRoad/Traffic
    ...

    階層數沒有固定,但是要特別注意的是,英文的大小寫是有區別的,且長度不可超過 65536 個字元。

    品質 ( Qos ) 設定

    MQTT 定義了三個層級的品質 ( Qos:0、1、2 ),分別適用於不同的情況:

    Qos 0:at most once 最多傳一次
    在「Qos:0」的設定下,訊息送出後就不管了,由於 MQTT 是屬於網路架構中的應用層,它並不會知道底層的網路斷線與否,所以 Broker 是有可能沒收到訊息的。如果你的目標裝置接的是有線網路,或者,遺漏幾筆資料也不會對結果造成太大影響的情況下 ( 樣本數夠多 ),可以使用這個設定。

    Qos 0:示意圖

    Qos 1:at least once 至少傳一次
    Broker 從 Publisher 收到「Qos:1」的訊息之後,會回應一個 PUBACK,以確認有收到訊息。如果連線中斷或其他狀況導致 Publisher 沒有收到 PUBACK,Publisher 就會重新發送,保證訊息至少傳送至 Broker 一次。

    Qos 1:示意圖
    然而,假設 Broker 有收到訊息,但在回應給 Publisher 時,中間發生斷線或故障。Publisher 會認為 Broker 沒有收到而重送,結果導致 Broker 重複收到同一份訊息。
    QoS1:Broker 有收到訊息 但 Publisher 沒收到 PUBACK

    Qos 2:exactly once 確實傳送一次
    「Qos:2」設定比「Qos:1」更嚴謹了一些。它將發送訊息分成了幾段:

    Broker 收到「Qos:2」的訊息之後,將回覆 PUBREC 給 Publisher,表示收到了即將發布的訊息,並暫存訊息的封包識別碼,以防止因斷線或逾時,需重新傳送而造成的重複。

    Publisher 如果收到了 PUBREC,會再傳送 PUBREL 給 Broker,告訴 Broker 可以釋放訊息了。此時 Broker 會把訊息傳送給 Subscriber,然後回應 PUBCOMP 告訴 Publisher 已經發送完畢,並刪除暫存訊息。

    Qos 2:示意圖

    相較於「Qos:1」,「Qos:2」會佔用比較多的網路和傳送時間,但能確實傳送一次訊息。

    最後,究竟為什麼要使用 MQTT?
    IoT picture from https://www.freepik.com/

    在 IoT 的世界裡,末端的裝置動輒數百上千,一來一往的數據傳輸量很是驚人,流量那麼大的情況下,網路使用費計算起來也是相當可觀,並不是每個地方都可以享有吃到飽的服務,因此,封包傳輸量較小、能一對多的 MQTT 就成了主流之一。

    除此之外,品質 Qos 的設計也可以讓不同的裝置甚至不同的架構需求,使用合適的品質設定來傳送資料,以符合最佳的應用情境。

    2021年6月10日 星期四

    SPI (Serial Peripheral Interface) 串列 (序列) 週邊介面

    隨著科技的演進,在單晶片微控制器及 SoC 的領域中,SPI 及 I2C 這二種串列 (序列) 介面變得十分常見。這二者與主機間通訊用的非同步串列通訊埠 RS-232 (UART) 非常不一樣。

    • 二個都是同步傳輸介面,主要是用於 CPU 和週邊晶片之間。
    • SPI 及 I2C 二者設計的主要目的在於減少 CPU 和週邊晶片之間的接腳數。
    • SPI 一般需要 4 條接線 (至少三條),而 I2C 則只要二條線,這和早期常用的並列匯流排動輒十數條接線有著明顯的差異。
    • SPI 的硬體結構簡單而且傳輸速度快,一般是 5M/10M/20Mbps 或是更快 (可以到 200Mbps),I2C 的傳輸速度則只有 100Kbps/400Kbps/1Mbps(/3.4Mbps/單向 5Mbps)。
    • SPI 是全雙工,I2C 則是半雙工.
    • SPI 使用硬體線路來指定 slave 晶片,I2C 則在傳送的第一個位元組上指定 (7bit位址)。
    • SPI 不提供交握機制,無法確認 slave 晶片是否有跟上。I2C 則有雙向的確認機制。

    SPI 簡介

    SPI 是 Serial Peripheral Interface 的縮寫,中文意思是串列週邊介面,該介面是由 Motorola 公司設計發展的高速同步串列介面,原先是應用在其 68xx 系列的 8 位元處理器上 (1985 年首次出現在 M68HC11 處理器上,並提供了完整之說明文件),用以連接 ADC、DAC、EEPROM、通訊傳輸 IC...等週邊晶片。由於具備有低接腳數,結構單純,傳輸速度快,簡單易用...等特性,目前已經成為業界慣用標準。不只是單晶片微控制器上有,許多新的 SoC 晶片直接就支援多組 SPI 介面,甚至普及到連模組化的產品 (如:手機用的 LCD 模組 (SDI 介面),相機模組) 及 3C 產品 (如: 數位相機用的記憶卡) 也都是使用 SPI 介面。

    SPI 架構及介面接腳

    SPI 為一主從式架構,通常有一個 Master (主設備) 和一個 (或多個) Slave (從設備)。介接方法及內部硬體結構很簡單, 如下面的示意圖:

    SPI 結構示意圖

    SPI 接腳名稱及意義

    接腳名稱中文名稱說明
    MOSI主出從入master 數據輸出、slave 數據輸入。
    MISO主入從出master 數據輸入、slave 數據輸出入。
    SCLK時脈訊號時脈信號、由 master 產生並控制。
    /SS晶片致能slave 選擇信號,由 master 控制。slave 只有在 /SS 信號為低電位時,才會對 master 的操作指令有反應。

    介接時只要把相同名稱接腳接在一起即可。

      SPI 的接腳有另一套常用的名稱:
    • SDO:Serial Data Out, 資料輸出 (不分主從)
    • SDI:Serial Data In, 資料輸入 (不分主從)
    • SCK:對應 SCLK
    • /CS:對應 /SS

    由於這種標示方法不分主從,資料輸出都是 SDO,資料輸入都是 SDI,所以介接時必需把二個設備的 SDO 和 SDI 接腳對接:master 的 SDO 接到 slave 的 SDI,master 的 SDI 接到 slave 的 SDO。

    在實際應用上,如果不需要由 slave 讀回資料時 (如:Output Port Expander,或者 DAC 晶片),則 MISO 接腳可以省略。有看到一部份資料說:只有一主一從時,/SS 訊號可以省略,只要將 slave 的 /SS 直接接地即可。對某些 slave 晶片來說這是不對的,因為有些 slave 晶片會拿 /SS 訊號的下降緣來識別 master 送來的第一個 bit。這是非常重要的錯誤回復機制,遇到這類 slave 晶片只把 /SS 接腳直接接地是不會動作的。另外後述的 Daisy-Chain 接法也需要這個 /SS 訊號的上昇緣才能運作。Daisy-Chain 模式用它 (/SS 訊號的上昇緣) 來栓鎖指令,如此多個 slave 晶片才能同時載入不同的指令。

    由上面的結構示意圖,我們很清楚的看出它其實是利用二組頭尾相連的位移暫存器 (Shift Rigister) 來完成 master 和 slave 之間的資料交換,而且是接收與發送同時進行 (全雙工)。而 SCLK 就是用來控制二者同步位移的時脈信號,它是由 master 產生送出給雙方的位移暫存器。這樣子的結構允許資料一位元一位元的傳送,可快可慢,甚至允許暫停 (可以暫停在任意點上,完全沒有限制),因為 SCLK 沒有變動時,雙方的 shift register 是不會有動作的。所以如果你的微控制器沒有硬體式的 SPI 可以用,直接用軟體加一般的 GPIO 接腳,就可以模擬 SPI master。(用軟體模擬 SPI slave 則不建議,因為如果我們不知道對方會送多快的 SCLK)。

    在介接方面,除了簡單的一主一從架構之外,SPI 也可以一個 master 連接多個 slave。接法上有二種,一種是利用多條獨立的 /SS 訊號,另一種則利用 Daisy-Chain 的方式。

    SPI Master 介接多個 Slave 的二種接法

    使用 Daisy-Chain 的方法,不需要額外的硬體接線腳來擴充 /SS 訊號,但是傳送及接收程式需要大改,非常的麻煩。另外還有一個障礙是,slave 晶片必需在同一個模式下工作,還有也不能有接腳缺省的情形 (有許多 DAC 晶片沒有 MISO 接腳),也因此常見的應用 Daisy-Chain 的例子都是用來串連多個同一型號的晶片,例如: 需要多組 DAC 或者是 ADC 一起工作的情況。另外要注意的是:並非每一顆 SPI 晶片都可以支援 Daisy-Chain 的接法,必需詳細檢視晶片的使用手冊是否有支援 Daisy-Chain 介接方法,或者是檢視讀取的指令格式是否符合 Daisy-Chain 的需求。否則到時候硬體接線接好了,Master 卻怎樣也無法命令 slave 晶片正常的工作。

    Daisy-Chain 的運作方法是以倍數擴充每次傳輸的指令/資料長度 (有二個 slave 晶片串在一起,傳輸長度就 x2;三個 slave 晶片串在一起,傳輸長度就 x3)。由於上一顆 slave 晶片的 MISO 是連接到下一顆 slave 晶片 MOSI,所以多出來的傳輸訊號很自然把先前的訊號擠出來,送到下一顆晶片。等到所有訊號都送出去了,再來就是利用 /SS 訊號的上昇緣來通知每一顆 slave 晶片:要給你的指令/資料已經準備好了,請把它栓鎖 (latch) 起來了。同樣的讀回資料也會串在一起。所以原本的驅動程式必需把要給各個 slave 晶片的指令暫存起來然後依 slave 晶片串連的次序將指令串起來再送出,收回來的資料也必需先裁切好再依 slave 晶片串連的次序分配給對應的接收單元。

    是故比較常用的是多條獨立的 /SS 訊號。有的 master 晶片是直接提供額外的 /SS1,/SS2,/SS3... 等接腳。有的則需要利用原本的 /SS 再以一組 3 to 8 解碼器 (或者是 4 to 16 解碼器) 配合 3~4 條 GPIO 接腳來產生需要的多組 /SS。更有的 SoC 乾脆提供多組 SPI 單元及多條獨立的 /SS 訊號 (當然的,代價是 master 晶片的接腳數比較多,單價拉高)。

    SPI 工作模式及時序圖

    使用 SPI,最麻煩一件事是確認周邊晶片的工作模式。SPI 一共有 4 種工作模式,但這 4 種模式的時序圖差異非常小,常令初學者 '霧剎剎' 抓不到重點。如下圖:

    各種工作模式下的 SPI 時序圖

    上圖的上半部是 SCLK 的時序,有兩種選擇: CPOL=0 或者 CPOL=1。中間及下半部是 MOSI MISO 的時序,一樣是有兩種選擇:中間是 MOSI MISO 是在 SCLK 的奇數次變化栓鎖資料,下半部則是 MOSI MISO 是在 SCLK 的偶數次變化栓鎖資料。

      解說:
    • SPI slave 晶片一般只支援一種工作模式,所以通常 master 必需牽就 slave 把工作模式設成和 slave 一致,才能正常運作。(因為二者內部均是位移暫存器,所以 MOSI 和 MISO 的時序需求是一樣)
    • 首先,第一個不同是 SCLK 的極性 (polarity),所謂極性其實是指 SPI 不工作時,SCLK 是停留在高電位還是低電位。CPOL=0 是 SCLK 在不工作時停留在低電位,CPOL=1 則是停留在高電位。
    • 再來要注意 slave 晶片是在 SCLK 的下降緣栓鎖資料,還是在 SCLK 的上升緣。要讓對方栓鎖資料,我們就必需把資料 Hold 住,保持穩定,所以 Slave 晶片在 SCLK 的下降緣栓鎖資料,相對的 master 就必需要在上升緣送出資料。反之,slave 晶片在 SCLK 的上升緣栓鎖資料,相對的 master 就必需要在下降緣送出資料。
    • 但是 CPHA 的定義並不是依上升緣/下降緣。CPHA 設定的是晶片栓鎖資料的時機是在 /SS 訊號下降之後,SCLK 的奇數次變化 (CPHA=0),還是偶數次變化 (CPHA=1)。
    • 所以 CPHA 配合 CPOL (SCLK 的極性) 設定,組合起來一共有 4 種工作模式。不過模式的命名順序出現了分歧沒有統一,現有的晶片出現了二種順序 (參閱 Wiki 網站 Serial Peripheral Interface Bus)。所以撰寫程式時不要只是把 Mode 設成相同的數值,小心 master 晶片和 slave 晶片二者的模式順序不同。不過仔細對照一下,差異只有 mode 的數值不同而已,CPOL 及 CPHA 的定義並無不同。
      SPI Mode
      Mode # of
      Model A
      Mode # of
      Model B
      CPOL CPHA 資料栓鎖時機MISO MOSI
      時序圖
      1 0 CPOL=0 CPHA=0 奇數次 上升緣 上半組
      0 1 CPOL=0 CPHA=1 偶數次 下升緣 下半組
      3 2 CPOL=1 CPHA=0 奇數次 下升緣 上半組
      2 3 CPOL=1 CPHA=1 偶數次 上升緣 下半組
      Model A:For ARM-based and Microchip PIC
      Model B:其他
    • 一旦正確辨識出 slave 晶片的工作模式,只要把 master 設定成一樣的工作模式即可正確的傳送指令及接收資料。

    結語:

      總結一下辨識的技巧:
    • 先確認 slave 晶片需求的 SCLK 極性,不工作時是在低電位還是高電位, 由此確認 CPOL 為 0 或 1。
    • 再由 slave 晶片 datasheet 中的時序圖確認 slave 晶片是在 SCLK 的下降緣栓鎖資料,還是在 SCLK 的上升緣。
    • 直接比對上面的模式表,確認出 CPHA 值為 0 或 1。
    • 確認傳輸時序圖是否正確 (是奇數組 SCLK 變化,還是偶數組 SCLK 變化)。

    補充說明一點,從 /SS 的下降緣到第一個 SCLK latch edge 有一小段時間的 delay,這一小段 delay 必需滿足 slave device 的規格需求,尤其是 SCLK 的頻率偏高的時候。否取 slave device 會無法正常工作。有些 MCU 的 SPI 硬體模組把這一段設成固定的 CLK 數; 有些 MCU 的 SPI 硬體模組則可以讓使用者自行調整。(2018/06/14)

    還有一點在圖中有標示,但上文沒有提到的是: SPI 的資料是 msb (Most Significant Bit)bit7 (MSB) 先傳 (傳輸的資料格式是 8 Bits 就 bit7 先傳,是 16 Bits 就 bit15 先傳,是 24 Bits 就 bit23 先傳),如果你是用軟體來模擬 SPI master 需注意到這一點。話雖如此,有些許 MCU 的 SPI port 是允許你把它改為 lsb (Least Significant Bit) 先傳,這是為了要能應付一些沒有依照標準設計的晶片,並不是標準狀況,也請小心。

    另外 SPI 只是硬體的傳輸協定,完全沒有提及定址(選擇晶片/暫存器位址),指令,資料長度...等等,這一部份是完全由 salve 晶片制定,master 想要控制 slave 動作必需完全依據 slave 晶片 datasheet 上的規範。

    下二張圖是 SPI Daisy-Chain 部份的接線圖及其時序圖。其中 SPI Master (MCU) 串接了二顆 Slave 晶片,Slave 晶片的指令/資料格式為 16 bits。接線圖上我們標示了 ➀ ➁ 二個點,就是後面時序圖的量測點。時序圖中的 (DOUTM)DINS1 是 SPI Master (MCU) 的 MOSI 及 Slave1 的 MOSI (DIN) 輸入接腳連線上的時序 (接線圖上點 ➀ 所量測到的訊號),(DOUTS1)DINS2 是 Slave1 的 MISO (DOUT) 輸出接腳和 Slave2 的 MOSI (DIN) 輸入接腳連線上的時序 (接線圖上點 ➁ 所量測到的訊號)。

    如前面說的 Slave 晶片的指令/資料格式為 16 bits,所以時序圖上的 Byte1 及 Byte2 二個合起來才是一組 16 bits 的 SPI 指令/資料。Byte3 及 Byte4 則是另一組。由於 SPI 的電路結構使用的是串列位移暫存器,所以我們其實可以把 Slave1 和 Slave2 合起來看,變成一個長度為 32 bits 的串列位移暫存器 (點 ➀ 的時序圖)。由 Slave1 的觀點來看: 前 16 bits 資料 ( Byte1 及 Byte2) 當然是先把自己的串列位移暫存器填滿,可是 Master 又接著送 Byte3 及 Byte4 進來,所以先前的 Byte1 及 Byte2 就又都被擠出去給 Slave2 了。由 Slave2 的觀點來看: 狀況看 Slave1 是一樣的,只不過前 16 bits 是原先 Slave1 肚子裡的垃圾,後 16 bits 才是由 Master 送來的 Byte1 及 Byte2

    所以 Master 送完 4 bytes 資料時,Slave1 收到了後二個 bytes,Slave2 收到了前二個 bytes。接著 /SS 接腳訊號向上拉,Slave1 和 Slave2 一起把資料栓鎖,開始解碼及相關動作。

      總結 SPI Daisy-Chain 的注意事項有三:
    1. 傳輸時,給遠端晶片 (離 MCU 的 MOSI 接腳比較遠的,即 Slave2) 的資料要先送 (Byte1 及 Byte2),再接著送給近端 (Slave1) 的資料 (Byte3 及 Byte4)。
    2. 每一次傳輸都必需是完整的 N 組晶片所需的指令/資料 (N 為 Chain 裡的 slave 晶片數,圖示為 N=2),否則遠端晶片會收到別人的資料 (上一次傳輸所送出的資料),產生不正確的動作。
    3. 還有一個狀況是除非晶片有支援 NOP (no operation) 的指令,否則無法只針對 chain 裡其中一顆晶片下達指令/資料 (原因如上一項所述)。例如:74HC595 就沒有 NOP 指令;但是 MAX 7219/7221 就有 NOP 指令可用。