使用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)
但是对于开发人员来说,这些细节是透明的。在开源软件包(清单1)中,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);
清单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开发板一起为嵌入式应用程序开发提供了一个可访问但功能强大的平台。

用户喜欢...

新品推荐#8 CC1352R SimpleLink多频段无线MCU

德州仪器CC1352R SimpleLink多频段无线微控制器(MCU)是一种多协议Sub-1和2.4GHz无线MCU。该MCU面向无线M-Bus,IEEE 802.15.4g,支持IPv6的智能对象(6LoWPAN),Thread,Zigbee,KNX RF,Wi-SUN,Bluetooth5低能耗和...


利用市场上基于FirstFarm®®M33的MCU - 第2部分:生命周期安全管理

对于基于微控制器的系统的开发人员而言,物联网(IoT),工业自动化或个人电子等快速增长应用的广泛设计要求往往会迫使系统功能,性能或功耗之间达成妥协。面对不断增长的安全威胁,在...


第一款基于Arm®Cortex®-M33的MCU - 第1部分:管理功耗和性能

开发人员不断发现自己正在努力平衡在各种功能和应用程序中以较低功耗实现更高应用程序性能的经常冲突的需求。这些应用包括物联网(IoT),工业自动化,医疗系统和消费设备。在这些应用...


使用SPI XiP Flash扩展微控制器程序存储器的原因和方法

随着微控制器应用变得越来越复杂,开发人员正在为应用固件使用更多的Flash程序存储器对于开始执行相对复杂的边缘计算的物联网(IoT)端点尤其如此。但是,有时应用程序可以扩展到需要外...


开发工具调整 MCU 的功耗

构成物联网 (IoT) 的众多器件关联在一起的其中一个原因是对低能耗的要求。为了满足这一要求,需要在一个整体策略内,从多个层面进行优化。一个成功的设计不仅需要选择低功耗的元器件,...


构建更有效的智能设备:第 1 部分 – 使用 MCU 和 PMIC 的低功耗设计

本系列文章分为三个部分,将探讨如何构建更有效的智能设备。第 1 部分(本文)讨论用于平衡功率和性能的低功耗设计方法。第 2 部分解决应对用户透明安全性的挑战。第 3 部分检讨满足设...


MCU提供基于硬件的物联网安全性

伦敦 - 安全突然成为一个热门话题。考虑到所有关于连接设备和实现物联网(IoT)设备的讨论,以及对网络攻击的潜在威胁的更多认识,这并不令人惊讶。认识到这一点,意法半导体和恩智浦半...


构建更有效的智能设备:第1部分 - 采用MCU和PMIC的低功耗设计

无线物联网和可穿戴电子产品依赖于能够延长电池寿命的低功耗设计。然而,紧张的电力预算与更多功能的需求相冲突,这些功能可能迫使开发人员转向使用大型电池和充电需求,这对用户来说...


为物联网设计添加高性能语音关键字定位:第2部分 - 使用MCU

编者注:使用新兴的高效算法类,任何开发人员现在都可以在低功耗,资源受限的系统上部署复杂的关键字定位功能。这个由两部分组成的系列文章的第一部分展示了如何使用FPGA实现这一目标...


物联网(IoT)的兴起 预计未来五年微控制器市场将稳步上升

旧金山 - 据市场研究公司IC Insights称,预计未来五年微控制器市场将稳步上升,主要原因是传感器的普及和物联网(IoT)的兴起。 IC Insights表示,预计今年MCU出货量将增长18%,达到306亿部。该...


快点?蓝牙MCU无线耳塞解决方案的关键

设计蓝牙无线耳塞时会遇到许多挑战。 同步这对耳塞对于可接受的操作至关重要。 这对于完全无线的解决方案来说可能是困难的,这就是为什么许多系统跳过此任务并在耳塞之间运行连线的原...


Microchip发布了支持快速物联网实施的32位MCU

Microchip Technology Inc.最近发布了两款物联网芯片,专门用于简化产品开发,同时加强物联网终端的安全性 - 随着物联网设备数量迅速增加,这一问题日益受到关注。 该 SAM L10和L11 SAM 是32位微控制...


使用CLC扩展PIC®MCU功能

可配置逻辑单元(CLC)是一种灵活的外设,可为PIC单片机创建片上自定义逻辑功能。 该外设允许用户指定信号组合作为逻辑功能的输入,并使用逻辑输出来控制其他外设和I / O引脚。 这为嵌入...


选择并应用适用于物联网的低功耗微控制器

对于电池供电的连接设备来说,能量消耗是至关重要的,以最大限度地延长电池更换之间的时间,甚至允许设备运行在环境能源之外。尽管许多嵌入式系统开发人员精通代码优化,但为物联网(...


三种破解MCU 技术,就是这么简单!

MCU的安全等级正在逐步提升,一些公司甚至推出了安全主控,这是很好的现象,说明大家越来越重视嵌入式领域的信息安全和程序安全了。但对于很多特殊行业,比如消费类电子产品,低成本...


无线 MCU 选型,你必须知道的六个套路

物联网市场的高速发展,无疑也在搅动半导体行业原有的格局,为了在物联网时代不掉队,各个芯片厂商都在努力发展适应市场需求的新产品品类。因为联网是物联网终端设备的基本需求,所...