数字型输入/输出外设仅有两种有效状态,习惯用on和off、high和low、打开和关闭、接通和断开等表示两种有效状态,譬如一个继电器触点的接通状态和断开状态、 一颗指示灯的on状态和off状态等。存储一个数字型输入/输出外设的状态信息仅需要一个二进制位。绝大多数mcu的可编程i/o引脚都可编程输出高电平和低电平, 这样的i/o引脚电平状态与相应接口电路即可控制数字型输出外设的状态,因此在mcu内部使用二进制位的“1”和“0”分别表示数字型输出外设的状态。 同时,通过读取mcu的i/o引脚的电平即可获取数字型输入外设的状态,并使用布尔型(boolean)变量保存该状态。
很多编程语言都支持布尔型变量,尤其支持嵌入式系统的编程语言,譬如c/c++语言。虽然布尔型变量的有效值仅为“1”和“0”,如果目标计算机系统不支持位操作和位寻址, 布尔型变量仍占用一个字节或更多二进制位来存储一个二进制信息。现在的mcu绝大多数都支持位操作和位寻址,譬如arm cortex-m系列微内核支持“bit-band”操作, 允许存取指令访问单个数据位(详见 [1]_ 的6.7节)。
按钮和led指示灯是最简单的数字型输入和输出外设,图4.1给出bluefi上的按钮和led指示灯的电路连接示意图。
图4.1 bluefi上的按钮和led指示灯的电路连接示意图
从上图中,我们不仅能够了解数字型输入/输出信号的电平电压、驱动电流、频率和复位期间的默认状态,还能了解如何读取数字型输入外设的状态到内部变量, 以及如何通过写外设存储区的地址单元来控制数字型输出状态。
bluefi的主mcu(nrf52840)的外部复位信号的有效电平为低电平(详见nrf52840的产品说明文档 [2]_ ),且内部带有上电复位(即冷复位)电路(图中黄色的电阻和电容), 图4.1给出最简单的外部复位电路:一个手动复位按钮,一端接地,另一端与“nrst”引脚连接。内部上电复位电路的电阻与mcu的工作电源连接,当外部手动复位按钮未被按下时保持复位引脚状态为高电平, 这个电平的电压显然与mcu的工作电压相等;当按下手动复位按钮时复位引脚的状态为低电平,这个电平的电压与电源地相同。当我们需要给nrf52840复位时,只需要按下复位按钮即可。 按下按钮时从“nrst”引脚强制施加低电平信号给mcu内核的内部复位电路单元将片上所有功能单元(含cpu)复位,当我们释放手动复位按钮后,片上的上电复位电路确保“nrst”引脚处于高电平, cpu开始工作。我们在第2.7节已经了解到mcu的多种复位源,在复位期间,nrf52840内部的“resetreas”寄存器(0x4000 0400地址单元)将保存本次复位的信号源, 应用程序可以根据这个寄存器的内容来识别复位源。单按一次bluefi的复位按钮是正常的系统复位,而连续双击bluefi的复位按钮,你会发现bluefi进入bootloader状态。 这个功能是使用“resetreas”寄存器的内容。
bluefi的a和b按钮是可编程的,两个按钮的电路连接完全相似(除了使用不同的i/o引脚),图4.1中仅给出a按钮的电路连接。a按钮的接口电路不仅包含片外的按钮, 还包含片内的可配置上拉/下拉电阻,由于a按钮的一端与mcu工作电源连接、另一端与p1.7引脚连接,当a按钮按下时p1.7引脚被强制与电源连接;如果p1.7的内部配置为下拉电阻, 当a按钮释放时p1.7引脚被下拉到电源地。通过读取p1.7引脚的状态确定a按钮的状态,当a按钮按下时读取状态的结果为“1”(即高电平),当a按钮释放时读取状态的结果为“0”(即低电平)。 当我们将a按钮的状态保存到一个布尔型变量时,如果不采用dma(直接存储器访问)方式,nrf52840的cpu的工作过程为:将p1.in寄存器(即0x5000 0810地址单元)读入cpu内部某个寄存器, 然后再将d7位的值(即p1.7引脚的状态)保存到布尔型变量(即“bit_band”区的某个地址单元)。
对于p1.7内部可配置的上拉/下拉电阻的使用,需要在bluefi初始化期间根据a按钮的电路进行编程配置。按照图4.1,使用arduino ide平台,a按钮的初始化和使用代码参考如下:
void setup() { // put your setup code here, to run once: pinmode(pin_button1, input_pulldown);}void loop() { // put your main code here, to run repeatedly: bool state_abtn = digitalread(pin_button1); if (state_abtn == high) { // a button be pressed } else { // a button be released }} 第3行代码是调用arduino内部函数“pinmode(pin_button1, input_pulldown)”将p1.7引脚(即与a按钮连接的i/o引脚)配置为输入模式且使用内部下拉电阻。在arduin ide平台, 有三种输入配置:浮空输入(input)、上拉输入(input_pullup)和下拉输入(input_pulldown)。第8行调用arduino内部函数“digitalread(pin_button1)”读取a按钮的状态, 由于按钮的状态为二进制型信息,所以将a按钮的当前状态暂存在布尔型变量“state_abtn”中。根据图4.1的电路结构,当a按钮被按下时布尔型变量“state_abtn”的值为“true”或“high”。 注意,“high”是arduino平台的布尔型常量,“true”是c/c++编程语言的标准常量。
bluefi有两颗亮起时颜色分别为红色和白色的led指示灯,他们的连接电路如图4.1所示,两颗led分别受p1.12和p1.14引脚控制。当程序将p1.out寄存器(即0x5000 0804地址单元) 的d12位置位时,p1.12引脚将输“1”(即高电平),红色led指示灯将亮起;当程序将p1.out寄存器的d12位清零时,p1.12引脚输入“0”(即低电平),红色led指示灯将熄灭。 bluefi与其他数字电路采用相同的设计习惯,i/o引脚为高电平时对应的电压等于mcu的i/o工作电压,低电平对应的电压等于电源地,按照前一章的bluefi电路原理介绍, nrf52840使用3.3v作为i/o引脚电压。根据红色led的正向压降、串联电阻的阻值和高电平的电压,我们可以计算出红色led亮起时的电流(简称on电流),这个电流的大小决定指示灯的亮度。
根据a按钮的状态控制红色led指示灯亮和灭的代码如下:
oid setup() { // put your setup code here, to run once: pinmode(pin_button1, input_pulldown); pinmode(led_red, output);}void loop() { // put your main code here, to run repeatedly: bool state_abtn = digitalread(pin_button1); if (state_abtn == high) { // a button be pressed digitalwrite(led_red, high); } else { // a button be released digitalwrite(led_red, low); }} 按照“..arduino15packagesadafruithardwarenrf520.20.5variantsbluefi_nrf52840variant.h“头文件中对bluefi的i/o引脚用法的定义, 只需要将上述代码中的“led_red”引脚名称替换为“led_white”,然后编译并下载修改后的代码到bluefi,可以使用a按钮控制白色led的亮和灭。
与红色led相比,你也许已经发现bluefi的白色led更亮一些。这说明,白色led指示灯on电流大于红色led。如果使用i/o引脚输出的高电平电压直接驱动led,并不断地减小led的串联电阻阻值, led的亮度将会不断地增加吗?如果假设i/o引脚输出的高电平电压是理想的(即内阻为0且功率足够大),这个问题的答案是肯定的。事实上,所有mcu的i/o引脚的驱动能力都是有限的, 按拉电流和灌电流两种指标分别指定每一个i/o引脚的驱动能力。当i/o引脚的驱动能力无法满足led指示灯on电流时,我们自然会想到外部驱动,如图4.1中使用外部npn三极管驱动白色led指示灯, 此时i/o引脚输出的拉电流被三极管放大数十倍(即三极管的放大倍数)作为白色led指示灯on电流。当外部数字型输出外设需要更大的负载电流时,或许需要多级结构(如达林顿结构)的三极管提高放大倍数。
对于mcu的可编程i/o引脚,除了可配置的上拉/下拉电阻、可编程为输入/输出模式等,还有更多可配置的结构。以nrf52840为例,我们需要进一步了解其内部的结构,如图4.2所示。
图4.2 nrf52840可编程i/o引脚的内部结构
在上图中,我们可以找到一个可编程输入/输出引脚的所有配置选项、输入通道、输出通道等。除了数字i/o功能之外,一个可编程输入/输出引脚也可以当作模拟i/o功能引脚使用, 图4.2中的“anaen”是编程配置一个引脚当作数字i/o或模拟i/o的控制位。关于模拟输入/输出的功能,详见下一节。在nrf52840的手册中,我们可以找到每一个可编程输入/输出引脚的 配置和控制相关的存储器地址和有效的控制位,“pinmode(pin,mode)”、“digitalread(pin)”和“digitalwrite(pin,value)”等基本数字型i/o接口都是通过编程这些存储单元而实现的。
已经了解数字型i/o的电路和软件接口之后,我们可以接着第3章最后一节的任务:为bluefi设计bsp,现在只涉及bluefi的数字i/o相关的部分,即两个输入按钮和两个led指示灯的bsp。 如果你是bluefi的二次开发(编程应用)用户,你将会如何使用按钮和led指示灯呢?bsp的目的是根据特定硬件电路封装api并加快二次用户开发的工作效率,譬如bluefi的两个按钮的配置 (需根据按钮的电路结构)等,用户只需调用bsp封装的api即可得到“按钮被按下/释放/长按“,或直接控制“红色led亮/灭/切换”等。
为了了解bsp的基本结构,我们首先来实现led控制的api
区块链安全的关键技术有哪一些
12v一85vled灯电路图
什么电动牙刷好用?电动牙刷性价比排行榜,学生党必看!
无线AP覆盖范围有哪些因素来决定
兄弟打印机怎么样_兄弟打印机的使用方法
可编程数字输入和输出详解
愚人节推荐!7款最能蒙人的键盘/鼠标
国内轨道交通实现自研“自复位液磁断路器”:自动驾驶技术关键基础部件
选择适合自己的六轴机器人
数字货币对金融会计存在什么影响
4大数据库首次发布!艾睿光电『红外开源平台』二期建设完成
中兴通讯和联通完成TAPI北向接口对接,实现端到端业务快速配置和管理
采用电路编码实现立靶精度测量系统的设计与研究
燧原科技第二代芯片产品已大规模落地
经常使用的半导体开关有哪几种
涨升不断 MLED底气何来?MLED能否告别“价格战”?
物联网都有哪些通信协议?
2299元 麦芒10手机正式开售
安防监控系统的供电模式
常用的电子元器件及其分类方法