账号:
密码:
智动化 / 文章 /

简化ARM Cortex-M0+物联网嵌入式设计
利用CircuitPython开发板
[作者 Digi-Key北美編輯群]   2018年04月24日 星期二 浏览人次: [12441]

很多嵌入式应用采用了高阶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)
图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)
图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)
图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)
图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)
图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开发板一起为嵌入式应用开发提供了一个易於使用却功能强大的平台。


相关文章
如何透过Simulink进行ISO 26262专案
对於8位元、32位元MCU的选择
实现MCU的低功耗设计
万物联网时代来临
加速推进安全物联网部署:从晶片到云端
comments powered by Disqus
  相关新闻
» Digi-Key与Analog Devices成为合作夥伴 共同推动创新MeasureWare平台
» 聚焦数位转型关键技术 2019 Arm科技论坛将於11/6台北、11/7新竹盛大展开
» Digi-Key获颁Electronics Maker 2019年最隹电子元件经销商奖
» Digi-Key宣布与Directed Energy建立全球独家经销合作关系
» 科专助攻智慧制造 成产业最隹後盾
  相关产品
» Digi-Key推出采用BQR可靠度数位解决方案的BOM MTBF预测服务
» Digi-Key宣布推出供应商主导的KiCad资料库
» Arm推出新一代Armv8.1-M架构
» 全新Arm ISP技术打造更锐利的数位之眼
» 瑞萨电子推出单一封装的简化型数位电源模组系列产品


刊登廣告 新聞信箱 读者信箱 著作權聲明 隱私權聲明 本站介紹

Copyright ©1999-2019 远播信息股份有限公司版权所有 Powered by O3
地址:台北市中山北路三段29号11楼 / 电话 (02)2585-5526 / E-Mail: webmaster@hope.com.tw