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 灯。
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
初始化
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
void pinMode( uint8_t pin, uint8_t mode )
{
/* 101 NUM_DIGITAL_PINS=32 */
if (pin >= NUM_DIGITAL_PINS) return;
/* g_APinDescription 为映射结构体数组 */
PinDescription *p = &g_APinDescription[pin];
...
}
PinDescription
映射配置结构体包含如下:
C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\cores\arduino\Arduino.h
typedef const struct _PinDescription
{
uint32_t ulGPIOId; // GPIO port pin
uint32_t ulGPIOPort; // GPIO port ID
uint32_t ulGPIOType; // LMT or SS
uint32_t ulGPIOBase; // GPIO register base address
uint32_t ulSocPin; // SoC pin number
uint32_t ulPinMode; // Current SoC pin mux mode
uint32_t ulPwmChan; // PWM channel
uint32_t ulPwmScale; // PWM frequency scaler
uint32_t ulAdcChan; // ADC channel
uint32_t ulInputMode; // Pin mode
} PinDescription;
g_APinDescription
就是提前配置好的全局结构体数组,输入 pin=LED_BUILTIN
,即编号十三引脚。
C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\variants\arduino_101\pins_arduino.h
static const uint8_t LED_BUILTIN = 13;
而十三引脚的映射如下:
C:\Users\XXX\AppData\Local\Arduino15\packages\Intel\hardware\arc32\2.0.2\variants\arduino_101\variant.cpp
PinDescription g_APinDescription[]={
...
{8, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_BASE_ADDR, 42, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE}, // Arduino IO13
...
};
mode=OUTPUT
把编号十三引脚设置为输出模式,进入如下分支(ulGPIOType=SOC_GPIO
):
void pinMode( uint8_t pin, uint8_t mode ) {
...
if (mode == OUTPUT) {
...
else if (p->ulGPIOType == SOC_GPIO) {
uint32_t reg = p->ulGPIOBase + SOC_GPIO_SWPORTA_DDR;
SET_MMIO_BIT(reg, p->ulGPIOId);
}
}
...
}
可以看出映射到的寄存器地址是 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
#define SOC_GPIO_BASE_ADDR 0xB0000C00
#define SOC_GPIO_SWPORTA_DDR 0x04
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]
是否使能上拉
Intel®Quark™Microcontroller Software Interface Pin Multiplexing Reference Guide ↩︎
Intel® Quark™ SE Microcontroller C1000 Datasheet, 3.5, Ballmap Name ↩︎