很多嵌入式應用採用了高階MCU,但它們只需基本的硬體控制功能,而無高階嵌入式設計的「硬實時」需求。開發人員和創客經常很容易陷到硬體設計、C/C++程式設計和即時操作系統的細節中。幸運的是,他們可以使用更簡單的方法。
本文介紹一種使用來自 Adafruit Industries 的微型開發板較為容易的方法。該開發板結合了Python程式設計語言的嵌入式設計變體與基於Arm Cortex-M0+處理器的高階32位MCU。
高階MCU簡化設計
高階MCU通過將全套類比和數位外設與功能強大的處理器內核整合在一起,從而?明簡化硬體設計。例如,Microchip 的 ATSAMD21G18 MCU將ARM Cortex-M0+內核、256 KB快閃記憶體、32 KB SRAM、高級控制子系統和大量外設全部整合在10 x 10 mm2的扁平(TQFP)封裝(圖 1)中。
圖1 : Microchip的 SAM D21 MCU系列成員都基於超低功耗ARM Cortex-M0+ 內核,提供全套功能塊和外設,差別僅在於具體的記憶體大小和外設通道數量。(source:Microchip) |
|
除了32個GPIO之外,ATSAMD21G18 MCU的外設集,還包括多個高級串列通信 (SERCOM) 通道、波形輸出通道、多通道12位模數轉換器 (ADC)、類比比較器、10位數模轉換器 (DAC)。
設計挑戰
有了此類高階MCU,開發人員無需花費時間查找和連接外部外設,但它們仍然對於在系統設計中部署MCU的方式提出了嚴格要求。例如,在整合多種類型的電路時,ATSAMD21G18 MCU的設計要通過相應的一組單獨域來提供電源。因此,開發人員必須處理處理器內核VDDCORE、內部穩壓器(VDDIN)、外設 (VDDIO)和類比模組(VDDANA)的多個電源和接地接腳(圖 2)。
在設計過程中,開發人員必須遵守具體的建議,包括提供電源、接地以及選擇和放置去耦電容器—這些對於經驗豐富的開發人員極為平常,但對於新接觸嵌入式 MCU 硬體設計的開發人員而言,卻是潛在的陷阱。
圖2 : ATSAMD21G18 MCU使用多個功率域為不同的類比和數位塊供電,在為這些域供電時需要多加注意。(source:Microchip) |
|
同樣,這些元件的軟體發展工作也是非常艱巨的。通常,新入門的嵌入式系統開發人員會發現他們埋頭於從嵌入式開發資料瞭解C/C++開發的相關細節,而這些資料更多地針對具有硬實時需求的應用。這些應用通常具有針對中斷延遲和確定性回應的關鍵性時序要求。但是,很多面向物聯網(IoT)的新興感測器設計對資料獲取或致動器工作的要求卻要寬鬆得多,或者說這些要求很容易滿足。
簡化嵌入式開發
Adafruit推出了一系列開發板,旨在幫助嵌入式開發人員消除這些硬體和軟體設計障礙,為許多應用需求提供了特別有效的解決方案。Adafruit 的 Metro M0 Express 和 Feather M0 Express 都基於ATSAMD21G18 MCU,提供的是完整的嵌入式系統,包括序列介面(USB、SPI、I2C和UART)、脈衝寬度調製(PWM)、中斷輸入,以及多個模擬IO和GPIO。這些開發板的差異僅在於尺寸和GPIO數量:2.8" x 2.1" x 0.28"的Metro M0 Express提供25個GPIO,而尺寸稍小(2.0" x 0.9" x 0.28")的Feather M0 Express則提供20個GPIO。
SAM D21 MCU系列使用了最高階的MCU,提供的外設通道數遠多於物理接腳,但提供的接腳映射功能可將外設功能分配給特定硬體接腳。因此,雖然尺寸小巧,但每個開發板都可使用共用接腳來提供MCU廣泛外設的全部功能(圖 3)。
圖3 : Adafruit利用接腳複用在微型Feather M0 Express開發板中提供大量 ATSAMD21G18外設功能子集。(source:Adafruit) |
|
但是,對於開發人員而言,這些細節是透明的。Adafruit在其開源套裝軟體的特定模組中為每個開發板提供了特定配置。
STATIC const mp_rom_map_elem_t board_global_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_PA02) },
{ MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_PB08) },
{ MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_PB09) },
{ MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_PA04) },
{ MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_PA05) },
{ MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_PB02) },
{ MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_PB11) },
{ MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_PB10) },
{ MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_PA12) },
{ MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_PA11) },
{ MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_PA11) },
{ MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_PA10) },
{ MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_PA10) },
{ MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PA22) },
{ MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PA23) },
{ MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_PA15) },
{ MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_PA20) },
{ MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_PA07) },
{ MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_PA18) },
{ MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_PA16) },
{ MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_PA19) },
{ MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PA17) },
{ MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_PA06) },
};
MP_DEFINE_CONST_DICT(board_module_globals, board_global_dict_table);
Adafruit開源CircuitPython資料庫摘錄了硬體詳細資訊,其中包括使用開發板特定的接腳映射,例如此處顯示的Feather M0 Express開發板映射。(代碼來源:Adafruit)
開始開發時,用戶可將開發板插入USB埠,並且將內置USB引導程式與 Arduino IDE 一起使用。為了進一步簡化引入嵌入式軟體設計,開發人員可以使用內置功能,輕鬆將CircuitPython載入到其電路板上,然後即可開始構建嵌入式應用。
利用CircuitPython簡化開發
CircuitPython旨在?明加快嵌入式開發的學習速度,它的功能實際上源自 MicroPython,後者是與Python關係更直接的派生語言。憑藉簡單清晰的語法和大量的支援模組,Python成為一種流行語言。但是,其代碼佔用空間過大,對嵌入式系統不實用。
MicroPython砍掉了Python的一些比較繁瑣的功能,簡化的版本能夠滿足嵌入式系統的邏輯約束,同時又保留了語言的核心功能。在開發CircuitPython的過程中,Adafruit更進一步,刪除了被視為對嵌入式系統新手程式時不太必要的模組。
Adafruit宣稱CircuitPython的目標是提供一種非常適合培訓的語言,讓開發人員能夠熟練掌握嵌入式設計,而無需糾纏於低級別開發細節。CircuitPython從前代產品Python繼承的最令人期待的特性之一是解釋型特性,讓開發人員能夠通過對話模式探索外部模組的介面。例如,CircuitPython的基本模組就是開發板模組—一個提供對相關開發板I/O接腳訪問的開發板特定模組。開發人員能夠從控制台啟動CircuitPython,導入該開發板模組並即時查看支援的接腳名稱。
>>> import board
>>> dir(board)
['A0', 'SPEAKER', 'A1', 'A2', 'A3', 'A4', 'SCL', 'A5', 'SDA', 'A6', 'RX',
'A7', 'TX', 'LIGHT', 'A8', 'TEMPERATURE', 'A9', 'BUTTON_A', 'D4', 'BUTTON_B',
'D5', 'SLIDE_SWITCH', 'D7', 'NEOPIXEL', 'D8', 'D13', 'REMOTEIN', 'IR_RX',
'REMOTEOUT', 'IR_TX', 'IR_PROXIMITY', 'MICROPHONE_SCK', 'MICROPHONE_DO',
'ACCELEROMETER_INTERRUPT', 'ACCELEROMETER_SDA', 'ACCELEROMETER_SCL',
'SPEAKER_ENABLE', 'SCK', 'MOSI', 'MISO', 'FLASH_CS']
在解析器控制台提示符處(>>),程式師可以導入開發板模組,並輸入 dir(board),以查看該開發板特定模組中提供的接腳名稱。(代碼來源:Adafruit)
開發板模組提供與底層硬體的連接,同時提供一種簡單方式來訪問Metro M0 Express和Feather M0 Express開發板的接腳。例如,A0模擬接腳被簡單引用為 "board.A0"。另一方面,各個模組中駐留有特定硬體功能,例如:analogio模組代表類比;digitalio模組代表數位;busio模組代表I2C、SPI和UART;pulseio 模組代表PWM和其他基於脈衝的協議等。因此,要在 CircuitPython中讀取A0 模擬輸入,只需導入相關模組,並讀取相關元件實例的值(清單 3)。
import board
import analogio
def adc_to_voltage(val):
return val / 65535 * 3.3
adc = analogio.AnalogIn(board.A0)
pinA0voltage = adc_to_voltage(adc.value)
與Python相同,CircuitPython提供了很多高級別模組,開發人員可將它們導入自己的代碼中;與Python不同,CircuitPython還提供了一些模組,讓程式師能夠執行硬體級別的操作,例如讀取值(adc.value)(在ADC輸入接腳 (board.A0)處)。(代碼來源:Adafruit)
開發人員可通過對類比或數位I/O接腳的直接訪問,輕鬆地擴展硬體功能。例如,他們可以通過試驗板將LED連接到開發板的A0連接(圖4),並且使用類比模組讓LED閃爍,以詳細研究模擬輸出特性。
圖4 : 開發人員可以通過將試驗板電路,例如具有限流電阻器的LED,連接到 Metro M0 Express板的A0模擬輸出,即可調出MCU的DAC,從而快速構建外部硬體原型。(source:Adafruit) |
|
import board
import analogio
led = analogio.AnalogOut(board.A0)
while True:
led.value = 65535 # max brightness
time.sleep(0.5) # stay on for 1/2 sec
led.value = 0 # off
time.sleep(0.5) # stay off for 1/2 sec
對於圖4所示的試驗板電路,開發人員使用CircuitPython analogio 模組,創建綁定到該板A0接腳的 Analogout 類實例(led),並修改其值屬性,以便控制 LED亮度。(代碼來源:Adafruit)
大多數現代「智慧」感測器和致動器都提供I2C或SPI介面,用於讀取、寫入和監視週邊設備。雖然開發人員可將器件輕鬆連接到開發板的SPI或I2C介面,但軟體介面可能需要額外的工作。
為了最大程度減少這類工作,Adafruit為一些流行的元件(例如 Silicon Labs 的 SI7021 溫度/濕度感測器)提供了CircuitPython模組。與類比I/O模組相同,在定義了所需的I2C介面物件之後,SI7021 CircuitPython 模組允許程式師只需使用相應類物件的實例即可訪問感測器。
import adafruit_si7021
from busio import I2C
from board import SCL, SDA
# create the I2C interface object
i2c = I2C(SCL, SDA)
# and use it to instantiate the sensor object
sensor = adafruit_si7021.SI7021(i2c)
# and perform the sensor measurements
current_temperature = sensor.temperature
current_relative_humidity = sensor.relative_humidity
Adafruit 開源軟體庫提供了簡化附加硬體功能訪問的CircuitPython模組,例如使用Silicon Labs的SI7021感測器的溫度和濕度測量。(代碼來源:Adafruit)
Adafruit板和CircuitPython開源庫的組合雖然主要是作為一個學習平臺,但也可用於創建相當先進的物聯網設備和其他嵌入式設計。同時,開發人員需要認識到,諸如MicroPython/CircuitPython之類解釋型語言,在滿足硬實時需求的能力方面有很大的局限性。但是,對於許多嵌入式應用而言,這個學習平臺可為擴展奠定堅實的基礎。
為了增加硬體功能,開發者可在Feather M0 Express板上疊接可用的 Adafruit FeatherWing 子卡,甚至可以使用 FeatherWing Proto 原型板添加他們自己的電路。為了增加對CircuitPython中的額外硬體功能的支援,開發人員必須創建定制軟體來添加所需的底層驅動程式。然而,通過將開放源碼庫與Python本身特性組合在一起,即使是這項工作也得到了最大程度的簡化。
通過檢查開源庫,程式師可以研究用於實現硬體支援的關鍵設計模式。例如,Adafruit的SI7021模組展示了相應的“Pythonic”類結構,包括構造函數和輔助函數。通過遵循這種方法,開發人員能夠以最小的工作量來添加自己的硬體。
from micropython import const
import ustruct
import sys
from adafruit_bus_device.i2c_device import I2CDevice
HUMIDITY = const(0xf5)
TEMPERATURE = const(0xf3)
_RESET = const(0xfe)
_READ_USER1 = const(0xe7)
_USER1_VAL = const(0x3a)
def _crc(data):
crc = 0
for byte in data:
crc ^= byte
for i in range(8):
if crc & 0x80:
crc <<= 1
crc ^= 0x131
else:
crc <<= 1
return crc
class SI7021:
"""
A driver for the SI7021 temperature and humidity sensor.
"""
def __init__(self, i2c, address=0x40):
self.i2c_device = I2CDevice(i2c, address)
self.init()
self._measurement = 0
def init(self):
self.reset()
# Make sure the USER1 settings are correct.
while True:
# While restarting, the sensor doesn't respond to reads or writes.
try:
data = bytearray([_READ_USER1])
with self.i2c_device as i2c:
i2c.write(data, stop=False)
i2c.read_into(data)
value = data[0]
except OSError as e:
if e.args[0] not in ('I2C bus error', 19): # errno 19 ENODEV
raise
else:
break
if value != _USER1_VAL:
raise RuntimeError("bad USER1 register (%x!=%x)" % (
value, _USER1_VAL))
def _command(self, command):
with self.i2c_device as i2c:
i2c.write(ustruct.pack('B', command))
def _data(self):
data = bytearray(3)
data[0] = 0xff
while True:
# While busy, the sensor doesn't respond to reads.
try:
with self.i2c_device as i2c:
i2c.read_into(data)
except OSError as e:
if e.args[0] not in ('I2C bus error', 19): # errno 19 ENODEV
raise
else:
if data[0] != 0xff: # Check if read succeeded.
break
value, checksum = ustruct.unpack('>HB', data)
if checksum != _crc(data[:2]):
raise ValueError("CRC mismatch")
return value
def reset(self):
self._command(_RESET)
@property
def relative_humidity(self):
"""The measured relative humidity in percents."""
self.start_measurement(HUMIDITY)
value = self._data()
self._measurement = 0
return value * 125 / 65536 - 6
@property
def temperature(self):
"""The measured temperature in degrees Celcius."""
self.start_measurement(TEMPERATURE)
value = self._data()
self._measurement = 0
return value * 175.72 / 65536 - 46.85
def start_measurement(self, what):
"""
Starts a measurement.
Starts a measurement of either ``HUMIDITY`` or ``TEMPERATURE``
depending on the ``what`` argument.Returns immediately, and the
result of the measurement can be retrieved with the
``temperature`` and ``relative_humidity`` properties.This way it
will take much less time.
This can be useful if you want to start the measurement, but don't
want the call to block until the measurement is ready -- for instance,
when you are doing other things at the same time.
"""
if what not in (HUMIDITY, TEMPERATURE):
raise ValueError()
if not self._measurement:
self._command(what)
elif self._measurement != what:
raise RuntimeError("other measurement in progress")
self._measurement = what
為了將自訂硬體添加到其CircuitPython應用中,開發人員可以使用像用於 SiLabs si7021的Adafruit CircuitPython驅動程式這樣的開源軟體。該驅動程式展示了使用隱式(__init__)和顯式(init)構造函數來設計感測器硬體類 (SI7021),以及通過串列匯流排(本例中為I2C匯流排)來訪問硬體本身的關鍵設計模式。(代碼來源:Adafruit)
其他模組,特別是資源庫的硬體抽象層(HAL)中的模組,提供了用於實現物理硬體訪問的較低級別C語言服務和hook。完成自訂模組後,開發人員可以利用分步說明,將自訂的C和Python代碼添加到環境中,這些分步說明描述了 Python、MicroPython和CircuitPython內置的特定hook的使用。在桌面或伺服器 Python環境中,增強過程在這一點即已結束,但在嵌入式環境中,則還需要額外的步驟,使用增強代碼映射來更新開發板的固件。
Adafruit為該開發板提供了內置的引導程式,可自動載入USB Flashing Format (UF2)映射。開發人員通過按下該開發板的RESET按鈕兩次來觸發引導程式進程,這會導致在使用者的主機檔案系統中出現一個新的“boot”可移動驅動器。開發人員只需將UF2映射從主機系統拖放到代表開發板的可移動驅動器即可(圖5)。這與最初用於載入CircuitPython的過程相同。在這種情況下,開發人員只需拖放使用自訂代碼構建的UF2映射。引導程式會自動執行,將新映射刷入該開發板。
圖5 : Adafruit通過為開發板提供引導程式簡化了映射刷寫,當通過按下開發板的 RESET 按鈕啟動時,導致BOOT可移動驅動器顯示在檔案系統中(本例中為MAC OS),開發人員只需將新的UF2映射拖放至該驅動器上。(source:Adafruit) |
|
總結
對於希望獲得嵌入式設計經驗的開發人員來說,針對「硬」即時需求提供的工具和技術顯得有些小題大做。同時,開發人員又希望可以隨時使用能夠提供廣泛類比和數位I/O功能的高階32位MCU。
Adafruit的開源CircuitPython包則提供了一個更簡單的開發環境,能夠滿足這些較簡單的需求。通過將CircuitPython與Adafruit的Metro M0 Express或Feather M0 Express開發板結合在一起,新手開發人員可以快速獲得嵌入式系統經驗,而更有經驗的開發人員則可以快速構建嵌入式應用原型。
CircuitPython與Adafruit開發板一起為嵌入式應用開發提供了一個易於使用卻功能強大的平臺。