使用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开发板一起为嵌入式应用程序开发提供了一个可访问但功能强大的平台。

用户喜欢...

德州仪器的AWR1642和IWR1642是集成DSP和MCU的单芯片76 GHz至81 GHz FMCW雷达传感器

AWR1642和IWR1642器件是独立的FMCW雷达传感器单芯片解决方案,可简化汽车雷达(AWR1642)和毫米波传感器在76 GHz至81 GHz频段的实施。它们采用德州仪器的低功耗45纳米RFCMOS工艺制造,该工艺能够单片...


华虹半导体推出12位SAR ADC IP助力超低功耗MCU平台

华虹半导体有限公司 今日宣布,基于其0.11微米超低漏电 (Ultra-Low-Leakage,ULL) 嵌入式闪存 (eFlash) 工艺平台,推出自主设计的超低功耗12位逐次逼近 (SAR)型模数转换器 (ADC) (12-Bit SAR ADC) IP,达到...


德州仪器宣布其SimpleLink™ MCU平台集成全新的Amazon FreeRTOS,实现快速云连接

德州仪器和亚马逊AWS为物联网设备实现端到端云连接提供持续支持 德州仪器(TI)近日宣布,SimpleLink™微控制器(MCU)平台集成全新的亚马逊FreeRTOS,帮助开发商快速而安全地将物联网(Io...


[原创] Microchip PIC18(L)F25xx83系列MCU CAN应用方案

Microchip公司的PIC18(L)F25xx83系列是具有CAN技术的低功耗高性能微控制器(MCU),系列产品中具有多种通信外设如CAN,SPI,两个...


[原创] On Semi RSL10多协议无线系统级芯片(SoC)开发方案

On Semi公司的RSL10是超低功耗的多协议无线系统级芯片(SoC),采用ARMR CortexRM3处理器(48MHz)和LPDSP32 DSP核(音频CODEC),支持蓝牙...


锐成芯微:助力中国MCU企业开发低功耗物联网市场

物联网的宗旨是万物皆可联网,借以构成庞大的应用系统,并打造智慧的生活环境。因此,物联网设备势必需要具备联网能力,与此同时还要兼顾成本和功耗。微控制器( MCU )作为物联网的核...


MCU延续性创新可以走多远?

对于MCU而言,一直持续在延续性创新的路上进发,硬件层面不断提高集成度,软件层面不断提升灵活性,再通过工艺的优化不断降低成本,因而,也在不断蚕食单一功能专用芯片的“地盘”。...


HOLTEK新推出HT45F0060 RGB三色LED调光控制MCU

Holtek新推出小封装的RGB三色LED调光控制MCU - HT45F0060,内建RGB三色LED驱动电路与单线串接接口,可实现单颗主控并驱动RGB三色LED或多颗串接控制达到各种连续性变化的动态灯光效果。适合应用于...


采用新型TI SimpleLink™以太网MCU将传感器连接到云端, 融合有线和无线连接

使用具有集成物理层的新型以太网MCU简化工业网关设计,在SimpleLink MCU平台上实现100%的代码兼容性 德州仪器(TI)近日在SimpleLink™微控制器(MCU)平台上引入了以太网连接,这是一个用于有...


通俗讲解单片机、ARM、MCU、DSP、FPGA、嵌入式错综复杂的关系!

首先,“嵌入式”这是个概念,准确的定义没有,各个书上都有各自的定义。但是主要思想是一样的,就是相比较PC机这种通用系统来说,嵌入式系统是个专用系统,结构精简,在硬件和软件...


[原创] NXP LPCXpresso54628低功耗ARM MCU开发方案

NXP公司的LPCXpresso54628是基于ARM Cortex-M4核的低功耗MCU,具有丰富外设,非常低功耗和增强的调试特性.ARM Cortex-M4 CPU采用3级...


[原创] TI TIDEP-0087电动汽车(EV)充电桩人机接口(HMI)参考设计

TI公司的TIDEP-0087是采用AM335x系列微处理器的电动汽车( EV)充电基础设施人机接口(HMI)参考设计,采用软件加速图像和Qt电...


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

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


MCU(单片机)工程师的十个不要,三个必须要

十个不要: 1、不要第一句话就说:给个代码吧!你应该想想为什么。当你自己想出来再参考别人的提示,你就知道自己和别人思路的差异。 2、初学者请不要看太多的书,那会误人子弟的。...


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

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


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

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