Arduino 101 GPIO 实现详解
最近买了一块 Arduino 101 开发版,准备学习研究一下。
首先从最简单的 GPIO 开始。
概述
要知道 GPIO 基本上都是 CPU 直接控制的,我们先查找一下 CPU 的资料。
CPU
主控制器是 Intel® Curie™ Module 1,其整体结构图如下:
这是一个集成的控制芯片,我们发现其中的真正微控制器其实是 Intel® Quark™ SE Microcontroller C1000 2,其结构框图如下:
GPIO
手册中 1.10 节概述了 GPIO 控制器如下:
GPIO Controller
- Provides 32 independently configurable GPIOs
- All GPIOs are interrupt capable, supporting level sensitive and edge triggered modes
- Debounce logic for interrupt source
- 16 additional GPIOs available via Sensor Subsystem
- 6 additional Always-on interrupt and wake capable GPIOs
概括来说 GPIO 由三部分组成:
- 32 个独立配置口(暂用
SOC_GPIO_32
代表) - 16 个额外通过传感器子系统提供(暂用
SS_GPIO_16
代表) - 6 个额外提供中断及唤醒口(暂用
AON_GPIO_6
代表)
分析
Arduino 自带的 Blink 程序就是控制 GPIO 高低,从而开关 LED 灯。
1 |
|
初始化
void pinMode( uint8_t pin, uint8_t mode )
- pin: Arduino 定义的 GPIO 编号
- mode: INPUT,INPUT_PULLUP,OUTPUT
那么这个函数是怎么一步步配置到寄存器的呢?
首先找到 pinMode
函数实现如下:
C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\cores\arduino\wiring_digital.c
1 |
|
PinDescription
映射配置结构体包含如下:
C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\cores\arduino\Arduino.h
1 |
|
g_APinDescription
就是提前配置好的全局结构体数组,输入 pin=LED_BUILTIN
,即编号十三引脚。
C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\variants\arduino_101\pins_arduino.h
1 |
|
而十三引脚的映射如下:
C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\variants\arduino_101\variant.cpp
1 |
|
mode=OUTPUT
把编号十三引脚设置为输出模式,进入如下分支(ulGPIOType=SOC_GPIO
):
1 |
|
可以看出映射到的寄存器地址是 0xB0000C04,
(reg = SOC_GPIO_BASE_ADDR + SOC_GPIO_SWPORTA_DDR)
C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\system\libarc32_arduino101\common\scss_registers.h
1 |
|
SET_MMIO_BIT
是在 0xB0000C04 地址 bit 8 位设置为 1。
查找手册相应说明如下:
即把 SOC_GPIO_32[8]
设置为输出,那么 LED 实际上到底是不是 GPIO[8]
呢?
原理图 LED 部分如下:
而 ATPSCK/IO2_3V_IO13
和 Curie 芯片有两处相连接,分别是 SPI1_M_SCK
和 ATP_SPI_S_SCK
,
在 Curie 中找到相应引脚信息如下:
如此证明之前的推测是正确的,SOC_GPIO_32[8]
控制着 LED 开关,GPIO 为高时,LED 亮,反之,LED 灭。
输出
void digitalWrite( uint8_t pin, uint8_t val )
- pin: 0~31
- val: HIGH,LOW
输入
int digitalRead( uint8_t pin )
- pin: 0~31
总结
由上面分析可知,程序是通过一个事先定义好的映射表来查找对应的寄存器地址,再来设置相应的数据。
较重要的几个文件是:
- wiring_digital.c:设置 GPIO 寄存器,包括初始化,读和写
- variant.cpp:配置映射表
- scss_registers.h:寄存器地址
配置
SOC_GPIO_32
GPIO_SWPORTA_DDR
设置输入/输出口PMUX_PULLUP [0..3]
设置是否使能上拉这是一个多路复用寄存器可以设置外部引脚是否为上拉输入,
寄存器和引脚的映射,我没有找到明确的解释,但是查看官方的软件说明3后,
我基本确定 4 × 32 的寄存器标号即EXTERNAL_PAD_XX
中XX
标号4。
如:GPIO[8] 为EXTERNAL_PAD_42
那么如果设置其为上拉,
则,PMUX_PULLUP[1] bit 10 = 1
。
ps,手册中有两种封装,因为我们用的是 Curie 芯片,该芯片使用的是 WLCSP 封装,所以查看引脚表格也是看该封装的。PMUX_SEL [0..5]
设置引脚复用
SS_GPIO_16
在手册中我没有找到该部分寄存器定义,以下根据程序得出
SS_GPIO0_SWPORTA_DDR
orSS_GPIO1_SWPORTA_DDR
设置输入/输出PMUX_PULLUP [0..3]
设置是否使能上拉PMUX_SEL [0..5]
设置引脚复用
AON_GPIO_6
GPIO_AON_SWPORTA_DDR
设置是输入/输出PMUX_PULLUP [0..3]
设置是否使能上拉PMUX_SEL [0..5]
设置引脚复用
读
SOC_GPIO_32
读取 GPIO_EXT_PORTA
寄存器状态
SS_GPIO_16
读取 SS_GPIO_EXT_PORTA
寄存器状态
AON_GPIO_6
读取 GPIO_AON_EXT_PORTA
寄存器状态
写
SOC_GPIO_32
- 写入
GPIO_SWPORTA_DR
寄存器 - 设置
PMUX_PULLUP [0..3]
是否使能上拉
SS_GPIO_16
- 写入
SS_GPIO_SWPORTA_DR
寄存器 - 设置
PMUX_PULLUP [0..3]
是否使能上拉
AON_GPIO_6
- 写入
GPIO_AON_SWPORTA_DR
寄存器 - 设置
PMUX_PULLUP [0..3]
是否使能上拉
- 1.Datasheet for the Intel® Curie™ Module ↩
- 2.Intel® Quark™ SE Microcontroller C1000 Datasheet ↩
- 3.Intel®Quark™Microcontroller Software Interface Pin Multiplexing Reference Guide ↩
- 4.Intel® Quark™ SE Microcontroller C1000 Datasheet, 3.5, Ballmap Name ↩