使用CircuitPython开发板简化基于ARM®Cortex®-M0 +的物联网嵌入式设计

许多嵌入式应用使用先进的MCU,但只需要基本的硬件控制功能,而不需要高级嵌入式设计的“硬实时”要求。开发商和制造商经常沉浸在硬件设计,C / C ++编程和实时操作系统的细节中。幸运的是,有一个更简单的方法。
本文将通过Adafruit Industries的一对微型开发板展示一种更易于使用的方法,它将Python编程语言的嵌入式设计变体与基于ARM Cortex-M0 +处理器的高级32位MCU相结合。

高级MCU简化了设计

先进的MCU通过集成全面的模拟和数字外设以及强大的处理器内核来帮助简化硬件设计。例如,Microchip Technology ATSAMD21G18 MCU采用10 x 10 mm薄型四方扁平封装(TQFP),结合了ARM Cortex-M0 +内核,256 KB闪存,32 KB SRAM,高级控制子系统和大量外设)包(图1)。
Microchip Technology SAM D21 MCU系列的图表
图1:基于超低功耗ARM®Cortex®-M0 +,Microchip Technology SAM D21 MCU系列产品全部提供了一整套功能模块和外设,仅在特定数量的内存和外设通道上有所不同。(图片来源:Microchip Technology)
除了32个GPIO外,ATSAMD21G18 MCU的外设包括多个高级串行通信(SERCOM)通道,波形输出通道,多通道12位模数转换器(ADC),模拟比较器和10位数字模拟转换器(DAC)。

设计挑战

这类先进的MCU消除了开发人员花时间查找和连接外部外围设备的需要,但是它仍然对MCU如何设计到系统中提出了严格的要求。例如,在集成多种类型的电路时,ATSAMD21G18 MCU的设计通过一组相应的域来供电。因此,开发人员必须处理处理器内核V DDCORE,其内部稳压器(V DDIN),外设(V DDIO)和模拟模块(V DDANA)(图2)的多个电源和接地引脚。
在设计过程中,开发人员需要遵循具体的电源供应,接地连接,选择和放置去耦电容的建议 - 这对于有经验的工程师来说并不是什么不寻常的事情,但是对于嵌入式MCU硬件设计的开发人员来说却是一个潜在的问题。
Microchip Technology ATSAMD21G18 MCU的图片
图2:Microchip Technology ATSAMD21G18 MCU使用多个电源域来提供各种模拟和数字模块,并且需要小心地为这些域供电。(图片来源:Microchip Technology)
同样,使用这些设备进行软件开发看起来势不可挡。通常情况下,新的嵌入式系统开发人员发现自己陷入了从嵌入式开发材料中学习C / C ++相关的细节,这些材料更适合于具有实时要求的应用程序。这些应用通常对中断延迟和确定性响应具有关键的时序要求。然而,物联网(IoT)的许多新兴传感器设计需要(或可以容易地容忍)对数据采集或致动器操作的更宽松的要求。

简化嵌入式开发

旨在消除嵌入式开发人员的这些硬件和软件障碍,Adafruit的一套开发板提供了一个针对各种应用需求的特别有效的解决方案。基于ATSAMD21G18 MCU,Adafruit Metro M0 Express和Feather M0 Express均提供完整的嵌入式系统,包括串行接口(USB,SPI,I 2 C和UART),脉宽调制(PWM),中断输入作为多个模拟IO和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。
与大多数高级MCU一样,SAM D21 MCU系列提供比物理引脚多得多的外设通道,但提供了引脚映射功能,可将外设功能分配给特定的硬件引脚。因此,即使尺寸较小,每个电路板也使用共用引脚来提供全面的MCU广泛的外设功能(图3)。
Adafruit Feather M0 Express开发板的示意图
图3:Adafruit利用引脚多路复用技术在其微型Feather M0 Express开发板上提供ATSAMD21G18外设功能的丰富子集。(图片来源:Adafruit)
但是对于开发人员来说,这些细节是透明的。Adafruit已经在其开源软件包(清单1)中为特定模块中的每块板提供了特定配置。
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);
清单1:Adafruit开源CircuitPython库使用特定于电路板的引脚映射来抽象硬件细节,例如这里显示的Feather M0 Express电路板。(代码来源:Adafruit)
要开始开发,用户可以将电路板插入USB端口,并使用带有Arduino IDE的内置USB Bootloader 。为了更简单地介绍嵌入式软件设计,开发人员可以使用内置功能轻松地将CircuitPython加载到其电路板上,并开始构建嵌入式应用程序。

使用CircuitPython简化开发

为了帮助简化嵌入式开发学习曲线,CircuitPython实际上是从Python的更直接的后代MicroPython中派生出来的。通过简单,清晰的语法和支持模块的广泛可用性,Python已经成为流行的语言。但是,它的代码足迹太大,不适用于嵌入式系统。
MicroPython削减了Python的一些体积庞大的功能,使得一个版本能够满足嵌入式系统的逻辑约束,同时保留了该语言的核心功能。在创建CircuitPython的过程中,Adafruit已经更进一步,消除了新的嵌入式系统程序员认为不必要的模块。
Adafruit与CircuitPython的目标是提供一种非常适合教育的语言,使开发人员能够熟练掌握嵌入式设计,而不会陷入低层次的开发细节。CircuitPython从Python传统中获得的一个令人满意的功能是其解释性,它允许开发人员交互式地探索外部模块的接口。例如,CircuitPython中的一个基本模块是电路板模块 - 一个电路板专用模块,用于访问相关电路板的I / O引脚。开发人员可以从控制台启动CircuitPython,导入电路板模块,并立即查看支持的引脚名称(清单2)。
>>> 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']
清单2:在解释器控制台提示符(>>)处,程序员可以导入board模块并进入dir(board)以查看特定于电路板模块中提供的引脚名称。(代码来源:Adafruit)
主板模块提供到底层硬件的连接,提供访问Metro M0 Express和Feather M0 Express主板引脚的简单方法。例如,A0模拟引脚简称为board.A0。另一方面,具体的硬件功能驻留在模拟模拟模块中,数字digitalio; 用于I 2 C,SPI和UART的busio ; 以及用于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)
清单3:与Python一样,CircuitPython提供了许多开发人员将自己的代码导入的高级模块; 与Python不同,CircuitPython还提供了让程序员执行硬件级操作的模块,例如读取adc.valueADC输入引脚(board.A0)的值()。(代码来源:Adafruit)
开发人员可以通过直接访问模拟或数字IO引脚轻松扩展其硬件功能。例如,他们可以通过将面板上的LED指示灯连接到电路板的A0连接(图4)并使用模拟模块使LED闪烁(表4)来探索模拟输出。
Metro M0 Express板的A0模拟输出的图像
图4:开发人员可以通过将具有限流电阻的LED等布线电路连接到Metro M0 Express板的A0模拟输出,从而实现MCU的DAC,从而快速构建外部硬件的原型。(图片来源: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:对于图4所示的展开式电路,开发人员使用CircuitPython analogio模块创建绑定到电路板A0引脚的Analogout类实例(led),并修改其值属性以控制LED的亮度。(代码来源:Adafruit)
大多数现代“智能”传感器和执行器都提供I 2 C或SPI接口,用于读取,写入和监视外围设备。虽然开发人员可以在设备轻松连接到任何一块板的SPI或I 2个 I2C接口,软件接口可能需要额外的努力。
为了尽量减少这种努力,Adafruit为一些流行的设备提供CircuitPython模块,例如Silicon Labs SI7021温度/湿度传感器。与模拟I / O模块一样,在定义了所需的I 2 C接口对象(清单5)之后,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
清单5:Adafruit开源软件库提供了CircuitPython模块,可以使用Silicon Labs SI7021传感器简化对附加硬件功能的访问,如温度和湿度测量。(代码来源:Adafruit)
虽然主要是作为一个学习平台,但是Adafruit开发板和CircuitPython开源库的结合可以用来创建相当复杂的物联网设备和其他嵌入式设计。同时,开发人员需要认识到,像MicroPython / CircuitPython这样的解释型语言在解决硬实时要求的能力方面有很大的局限性。不过,对于许多嵌入式应用来说,这个学习平台为扩展提供了坚实的基础。
为了增加硬件功能,开发者可以在Feather M0 Express板上堆叠可用的Adafruit FeatherWing子卡,甚至可以使用FeatherWing Proto原型板添加他们自己的电路。为了增加对CircuitPython中额外硬件功能的支持,开发人员需要创建定制软件来添加所需的底层驱动程序。然而,即使是这样的努力,通过开放源码库和Python本身的性质的结合,也被最小化了。
通过检查开源库,程序员可以研究用于实现硬件支持的关键设计模式。例如,Adafruit的SI7021模块演示了一个适当的“Pythonic”类结构,包括构造函数和辅助函数(清单6)。通过遵循这种方法,开发人员可以以最小的努力添加自己的硬件。
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
清单6:为了将自定义硬件添加到他们的CircuitPython应用程序中,开发人员可以使用开源软件,例如SiLabs si7021的Adafruit CircuitPython驱动程序,该驱动程序演示了SI7021用隐式(__init__)和显式(init)构造函数,并通过串行总线(本例中为I 2 C总线)访问硬件本身。(代码来源:Adafruit)
其他模块,特别是在图书馆的硬件抽象层(HAL)中,提供了较低级别的C语言服务,并实现了对物理硬件的访问。完成自定义模块后,开发人员可以利用可用的分步配方来描述Python,MicroPython和CircuitPython内置的特定钩子的使用,以将自定义的C和Python代码添加到环境中。虽然增强过程在桌面或服务器Python环境的这一点结束,但是嵌入式环境需要额外的步骤来使用增强的代码图像更新电路板的固件。
Adafruit为主板提供了一个内置的自举程序,可自动加载USB Flashing Format(UF2)图像。开发人员通过按下电路板的RESET按钮两次来触发引导加载程序进程,这会导致在用户的主机文件系统中出现一个新的“引导”可移动驱动器。开发人员只需将UF2映像从主机系统拖放到代表主板的可移动驱动器(图5)。这与最初加载CircuitPython的过程相同。在这种情况下,开发人员只需删除使用自定义代码构建的UF2图像。引导加载程序会自动执行以使新图像闪烁。
Adafruit的图像通过提供一个引导程序来简化闪烁
图5:Adafruit通过向开发板提供引导加载程序来简化闪存,当通过按下开发板的RESET按钮启动引导程序时,引导BOOT可移动驱动器出现在其文件系统(此处显示的MAC OS)中,开发人员只需将其新的UF2映像。(图片来源:Adafruit)

结论

对于希望获得嵌入式设计经验的开发人员来说,解决“硬”实时需求所需的工具和技术只是过分的。与此同时,开发人员希望能够随时访问能够提供广泛的模拟和数字IO功能的先进的32位MCU。
Adafruit的开源CircuitPython包提供了一个更简单的开发环境,能够满足这些更简单的要求。通过将CircuitPython与Adafruit的Metro M0 Express或Feather M0 Express开发板相结合,新开发人员可以快速获得嵌入式系统的经验,而更有经验的开发人员可以快速构建嵌入式应用程序原型。
CircuitPython和Adafruit开发板一起为嵌入式应用程序开发提供了一个可访问但功能强大的平台。
Digi-Key电子商标

用户喜欢...

ARM:为物联网时代打造安全可靠的平台

mbed平台保障端点到云端安全 提供迈入物联网时代之基...


Google 的神秘物联网操作系统“Fuchsia” UI 首次曝光

Google 在去年 8 月左右才首次让外界察觉的新操作系统“Fuchsia”,当时还只是 GitHub 上的一连串程序代码。不过近期...


前进物联网新世界有更简单的方法!

这个世界已经变了…要取得各种物联网应用所需IP与专业知识不但没有想象中那么困难,而且能协助物联网装置设计者...


“真”物联网操作系统来了,ARM入场

在今天北京的ARM年度技术论坛上,ARM推出了专门针对IoT领域的mbed物联网设备平台(这次是中国首秀,事实上他们10月...


ARM与创新微携手开启物联网新时代

2016年12月5日,创新微科技有限公司与ARM公司在深圳君悦酒店举行了盛大的IoT战略签约会。创新微在蓝牙智能硬件领域...


基于ARM和蓝牙的无线信号采集系统的设计与实现

无线测试技术在工业领域有广泛的应用前景。在连线复杂并需要反复拆装被测设备和测试设备之间连线的场合,使用...


基于ZigBee与ARM的远程监控系统设计与实现

摘要:针对有线组网方式布线复杂、维护困难的问题,提出了一种基于ZigBee无线组网技术和嵌入式Web服务器,对家庭...


EM358x SoC系列为物联网应用提供无与伦比的无线性能、能效和可靠性

Silicon Labs(芯科实验室有限公司)近日宣布扩展了其业界领先基于ARM®架构的Ember® ZigBee®片上系统(SoC)产品组合,该系列产品为物联网(IoT)提供了无与伦比的无线性能、能效和可靠性。Si...


基于 ARM 与嵌入式 Linux 的 GPRS 无线通讯系统

本文主要介绍了以 SAMSUNG ARM9(S3C2410)和嵌入式 Linux 为平台的,基于 BenQ M22 模块的 GPRS 无线通讯系统的硬件和软件方面的设计。...


基于ARM的嵌入式RIFD读写器设计

本文选用射频芯片S6700,控制模块用的是S3C44B0X 作为微处理器,从硬件电路的设计和软件设计两个方面来阐述了嵌入式的RFID读写器的设计方案。...


ARM9系统在无线发射台智能化监控系统中的应用

本文阐述了嵌入式ARM9 系统在发射台自动化系统中的应用,主要实现对发射机的自动化监控,其良好的可在线编程功能,大大增进了友射台管理和减少因软件更新所带来的维护成本。...