GPIO(General Purpose I/O Ports)意思为通用输入/输出端口,通俗地说,就是一些引脚,可以通过它们输出高低电平、或者通过它们读入引脚的状态——是高电平还是低电平。

三星Exynos4412,它有304个 GPIO,分为GPA0、GPA1、GPB、GPC0、GPC1等共37组。可以通过设置寄存器来确定某个引脚用于输入、输出还是其它特殊功能。比如可以设置GPC0、GPC1作为一般的输入引脚、输出引脚,或者用于AC97、SPDIF、I2C、SPI口。

GPIO的操作是所有硬件操作的基础,由此扩展开来可以了解所有硬件的操作,这是底层开发人员必须掌握的。

Exynos4412芯片的GPIO寄存器:

既然一个引脚可以用于输入、输出或其它特殊功能,那么一定有寄存器用来选择这些功能;对于输入,一定可以通过读取某个寄存器来确定引脚的电平是高还是低;对于输出,一定可以通过写入某个寄存器来让这个引脚输出高电平或低电平;对于其它特殊功能,则有另外的寄存器来控制它——这些特殊功能现在先不关注。

如上推测,对于这几组GPIO引脚,它们的寄存器是相似的:GPXXCON用于选择引脚功能,GPXXDAT用于读/写引脚数据;另外,GPXXPUD用于确定是否使用内部上拉/下拉电阻。

GPXXCON寄存器:

从寄存器的名字即可看出,它用于"配置"(Configure)——选择引脚的功能。该寄存器中,使用4位来配置1个引脚。比如下图1:

图1. GPIO配置寄存器

  • 当GPA0CON的bit[31:28]值为0b0000时,引脚GPA0被设置为输入引脚;
  • 当GPA0CON的bit[31:28]值为0b0001时,引脚GPA0被设置为输出引脚;
  • 当GPA0CON的bit[31:28]值为0b0010时,引脚GPA0被设置为特殊功能UART引脚;
  • 当GPA0CON的bit[31:28]值为0b0011时,引脚GPA0被设置为特殊功能I2C引脚;
  • 当GPA0CON的bit[31:28]值为0b1111时,引脚GPA0被设置为中断引脚。

GPXXDAT寄存器:

用于读/写引脚:当引脚被设为输入时,读此寄存器可知相应引脚的电平状态是高还是低;当引脚被设为输出时,写此寄存器相应位可令此引脚输出高电平或低电平。

GPXXPUD寄存器:

使用2位来控制1个引脚:值为0b00时,相应引脚无内部上拉/下拉电阻;值为0b01时,使用内部下拉电阻;值为0b11时,使用内部上拉电阻;0b10为保留值。

所谓上拉电阻、下拉电阻,如图2所示:

图2. 上拉电阻和下拉电阻

上拉电阻、下拉电阻的作用在于,当GPIO引脚处于第三态(既不是输出高电平,也不是输出低电平,而是呈高阻态,即相当于没接芯片)时,它的电平状态由上拉电阻、下阻电阻确定。

怎样使用软件来访问硬件:

单引脚的操作无外乎三种:输出高低电平、检测引脚状态、中断。对某个引脚的操作一般通过读写寄存器来完成。

比如对于图3的电路,可以设置GPM4CON寄存器将GPM4_0、GPM4_1、GPM4_2和GPM4_3设为输出功能,然后写GPM4DAT寄存器的相应位使得这4个引脚输出高电平或低电平:输出低电平时,相应的LED点亮;输出高电平时,相应的LED熄灭。

还可以设置GPX3CON寄存器将GPX3_2、GPX3_3、GPX3_4、GPX3_5设为输入功能,然后通过读出GPX3DAT寄存器并判断bit2、bit3、bit4、bit5是0还是1来确定按键是否被按下:按键被按下时,相应引脚电平为低,相应位为0;否则为1。

那么,怎么访问这些寄存器呢?通过软件,读写它们的地址。比如,Exynos4412的GPM4CON、GPM4DAT寄存器地址分别是0x110002E0、0x110002E4,可以通过如下的指令让GPM4_0输出低电平,点亮LED1:

#define GPM4CON (*(volatile unsigned long *)0x110002E0)
#define GPM4DAT (*(volatile unsigned long *)0x110002E4)
#define GPM40_OUT (1<<0)
#define GPM40_MSK (0xf<<0)
GPM4CON &= ~GPM40_MSK;     //GPM4_0引脚对应的4位清零(因为我们不知道它原来的值是多少,清零最可靠)
GPM4CON |= GPM40_OUT;  //GPM4_0引脚设为输出
GPM4DAT &= ~(1<<0);  //GPM4_0输出低电平

图3. LED与按键连线图

以某种协议的接口访问硬件:

比如UART、I2C、SPI等接口,我们只需要根据协议要求做好相关设置,然后把数据填入某个寄存器,这类接口部件就会把数据按某种格式发送出去。

如图4所示,以UART为例:设置好UART的波特率等格式后,把要发送的数据写入UART的寄存器,UART控制器就会把数据逐位发送出去。

图4. 协议类接口示例UART

GPIO操作实例:LED和按键:

从这节开始,将涉及在单板上运行程序了,下面用几个例子由简到繁地介绍。LED和按键与处理器的电路连接如图3所示。

本小节有3个实例,通过读写GPIO寄存器来驱动LED、获得按键状态。先使用汇编程序编写一个简单的点亮LED的程序,然后使用C语言实现了更复杂的功能。

示例1:使用汇编代码点亮一个LED

只是简单地点亮发光二极管LED1。本实例的目的是让读者对开发流程有个基本概念。

主要有三个文件,首先,是led.S文件,里面全是汇编语言来完成的。已经做了详细的注释,不再讲解。

.text                                //表示下面的代码是text段
.global _start                      //定义个全局标号
_start://设置GPM4_0为输出引脚ldr r0, =0x110002E0              //将0x110002E0数字保存的R0寄存器中ldr r1, [r0]                    //将R0中保存的0x110002E0地址中的数据取出来,保存到R1寄存器bic r1, r1, #0xF               //将R1寄存器中的数据低四位清零orr r1, r1, #0x01              //将R1寄存器中的数据bit0置1str r1, [r0]                  //将R1寄存器中的数据保存到R0寄存器中的数据表示的地址中//设置GPM4_0输出低电平ldr r0, =0x110002E4               //将0x110002E4数字保存的R0寄存器中ldr r1, [r0]                    //将R0中保存的0x110002E4地址中的数据取出来,保存到R1寄存器bic r1, r1, #0x1               //将R1寄存器中的数据bit0清零str r1, [r0]                  //将R1寄存器中的数据保存到R0寄存器中的数据表示的地址中halt:                             //死循环b halt

值得注意的是:要修改寄存器的某些位时,最好先把原值读出并修改这些位,再把值写回去。寄存器里每位都有它的作用,这样做可以避免影响到其他位。

下面的是链接脚本文件leds.lds中的内容;很简单,如果有不明白的可以网上查询。

SECTIONS {. = 0x02023400;                           //链接地址,也就是说,希望程序运行在此地址.text : { *(.text) }                  //代码段.rodata ALIGN(4) : {*(.rodata)}            //只读数据段.data ALIGN(4) : { *(.data) }            //数据段.bss ALIGN(4) : { *(.bss) *(COMMON) }  //bss段
}

最后,介绍Makefile文件的内容:

led.bin : led.Sarm-linux-gcc -c -o led.o led.S                   #预处理、编译、汇编,不链接arm-linux-ld -Tleds.lds -g led.o -o led.elf        #链接arm-linux-objcopy -O binary -S led.elf led.bin   #将ELF格式文件转换为BIN文件arm-linux-objdump -D led.elf > led.dis          #反汇编clean:rm *.o *.elf *.dis *.bin

make指令比较第1行中文件led.bin和文件led.S的时间,如果led.S的时间比led.bin的时间新(led_on.bin未生成时,此条件默认成立),则执行第2~5行的命令重新生成led.bin及其他文件。也可以不用指令make,而直接一条一条地执行2~5行的指令——但是这样效率比较低。

第2行的指令是编译,第3行是连接,第4行是把ELF格式的可执行文件led.elf转换成二进制格式文件led.bin,第5行是得到它的反汇编文件(目前没用到该文件)。

执行"make clean"时强制执行第7行的删除命令。

注意:Makefile文件中相应的命令行前一定要有一个制表符(TAB)。

如果对Makefile中每句是详细意思,请参考交叉编译工具链的使用。

现在,来介绍将led.bin文件烧写到SD卡中,然后插入到开发板的SD卡槽中,设置启动模式,然后开启电源运行。

把SD卡接入读卡器,让VMware软件位于窗口最前面,然后把读卡器接入电脑。

点击VMware菜单"VM"->"Removable Devices",找到新出现的USB存储设备,点击"Connect"。

注意:如果菜单里没有新的设备出现,原因是VMware的USB服务未启动。可用以下方式启动:打开"控制面板"->"系统和安全"->"管理工具"->"服务",找到"VMware USB Arbitration Service",双击启动它。然后重启VMware软件。

然后执行以下命令,一般最后一个"不带数字的"设备节点就是SD卡的设备名:

$ ls /dev/sd*
输出示例:
brw-rw---- 1 root disk 8,  0 Nov 18 09:22 /dev/sda
brw-rw---- 1 root disk 8,  1 Nov 18 09:22 /dev/sda1
brw-rw---- 1 root disk 8,  2 Nov 18 09:22 /dev/sda2
brw-rw---- 1 root disk 8,  5 Nov 18 09:22 /dev/sda5
brw-rw---- 1 root disk 8, 16 Nov 18 17:42 /dev/sdb
brw-rw---- 1 root disk 8, 17 Nov 18 17:42 /dev/sdb1

在上面的输出示例中,/dev/sdb就是该卡的设备名。

首先,将led.bin文件拷贝到sd_fusing.sh文件的目录下,也就是同一个目录。执行如下命令将led.bin写入SD卡:

$ sudo sd_fusing.sh /dev/sdb led.bin

注意看该命令的输出信息,若有"source file image is fused successfully."即表示烧写成功。否则请根据错误信息解决。

注意:sd_fusing.sh的作用后面会详细讲解。

把SD卡取下接到开发板上,开发重新上电即可看到LED1灯被点亮了。

汇编语言可读性太差,这次用C语言来实现了同样的功能,而以后的实验也尽量用C语言实现。

示例2:使用C语言代码点亮一个LED

C语言程序执行的第一条指令,并不在main函数中。生成一个C程序的可执行文件时,编译器通常会在我们的代码中加上几个被称为启动文件的代码——crt1.o、crti.o、crtend.o、crtn.o等,它们是标准库文件。这些代码设置C程序的堆栈等,然后调用main函数。它们依赖于操作系统,在裸板上这些代码无法执行,所以需要自己写一个。

led.S中的代码很简单,关键指令只有2条。文件内容如下:

.text
.global _start
_start:ldr sp, =0x02027400         //调用C函数之前必须设置栈,栈用于保存运行环境,给局部变量分配空间;//参考ROM手册P14,我们把栈指向BL2的最上方;//即:0x02020000(iROM基地址)+5K(iROM代码用)+8K(BL1用)+16K(BL2用)bl main                      //跳转到C函数中执行halt:                            //死循环b halt

它在第4行设置好栈指针后,就可以通过第8行调用C函数main了──C函数执行前,必须设置栈。

现在,可以很容易写出控制LED的程序了。main函数在main.c文件中,代码如下:

//定义两个宏,方便操作使用到的寄存器
#define GPM4CON (*(volatile int *)0x110002E0)
#define GPM4DAT (*(volatile int *)0x110002E4)int main()
{//设置GPM4_0引脚为输出GPM4CON &= ~0xF;           //GPM4CON寄存器的低4位清零GPM4CON |= 0x1;          //GPM4CON寄存器的bit0置1,设置为输出引脚//设置GPM4_0引脚为低电平GPM4DAT &= ~0x1;            //GPM4DAT寄存器bit0清零,输出低电平return 0;
}

程序很简单,值得一提的是寄存器的操作方法。以GPM4CON为例,它被定义为:

#define GPM4CON (*(volatile unsigned int *)0x110002E0)

要理解上述宏定义,先看看以下代码:

int a;
int *p;
p = &a;
*p = 123;

指针p指向变量a,即p等于变量a的地址,假设a的地址为0xAABB,那么相当于:

int *p = (int *)0xAABB

要修改地址为0xAABB的内存值为0x123,只需要执行:

*p = 0x123;

要偷懒,连"*"号都不想写,怎么做呢:

#define PP (*(int *)0xAABB)
PP = 0x123;

所以,读写GPM4CON的实质,就是读写地址为0x110002E0的空间。

最后来看看Makefile:

led.bin : led.S main.carm-linux-gcc -c -o led.o led.S            #预处理、编译、汇编、不链接arm-linux-gcc -c -o main.o main.c     #预处理、编译、汇编、不链接arm-linux-ld -Tleds.lds -g led.o main.o -o led.elfarm-linux-objcopy -O binary -S led.elf led.binarm-linux-objdump -D led.elf > led.disclean:rm *.o *.elf *.dis *.bin

和上面那个Makefile文件几乎没有差别,这里不做详细介绍。

最后,执行make命令生成可执行文件 led.bin。然后,使用上面介绍的烧写SD卡的方法,将其烧写到SD中,插入SD卡到开发板,上电运行。查看LED1被点亮。

示例3:使用按键来控制LED

程序功能为:按下K1/K2/K3/K4则点亮LED1/LED2/LED3/LED4,松开按键则熄灭相应的LED——即用按键来控制灯。

key.c文件的代码如下:

#define GPM4CON (*(volatile int *)0x110002E0)
#define GPM4DAT (*(volatile int *)0x110002E4)#define GPX3CON (*(volatile int *)0x11000C60)
#define GPX3DAT (*(volatile int *)0x11000C64)int main()
{unsigned int val = 0;int i;//配置GPM4CON的0~3引脚为输出GPM4CON &= ~(0xFFFF);GPM4CON |=   0x1111;//配置GPX3CON的2~5引脚为输入GPX3CON &= ~(0xFFFF00);while(1){val = GPX3DAT;if(val & (1<<2)){GPM4DAT |= (0x1 << 0);}else{GPM4DAT &= ~(0x1 << 0);}if(val & (1<<3)){GPM4DAT |= (0x1 << 1);}else{GPM4DAT &= ~(0x1 << 1);}if(val & (1<<4)){GPM4DAT |= (0x1 << 2);}else{GPM4DAT &= ~(0x1 << 2);}if(val & (1<<5)){GPM4DAT |= (0x1 << 3);}else{GPM4DAT &= ~(0x1 << 3);}}return 0;
}

上面的程序很简单,首先,设置GPM4的0/1/2/3引脚为输出,配置GPX3CON的2~5引脚为输入;然后再while循环里面判断是否有KEY按下,如果有则点亮相应的LED。不做详细解读。

  1. 按前述方法把led.bin烧入SD卡,并用它来启动开发板;
  2. 按下/松开KEY1/2/3/4,观察LED1/2/3/4的状态。

好了,本文基本介绍完了。但是,应该有人会有疑问,为什么把链接地址设置为0x02023400,从SD卡启动时的CPU启动流程是什么? 以及烧写脚本是干什么的等等疑问?那我们就在下篇文章中做详细的解读。

tiny4412开发板GPIO试验相关推荐

  1. Air724开发板GPIO试验

    main.lua --必须在这个位置定义PROJECT和VERSION变量 --PROJECT:ascii string类型,可以随便定义,只要不使用,就行 --VERSION:ascii strin ...

  2. tiny4412开发板LED灯驱动写法

    简介 led灯成本很低,操控简单,在嵌入式产品中不可或缺,可以作为调试标识,状态指示等等,高级用法还可以作为呼吸灯进一步增强其美观性.本章介绍只控制led灯的亮灭,tiny4412开发板有四个LED灯 ...

  3. tiny4412开发板实现uboot引导启动android 5.0.2系统

    1目的 实现uboot引导启动android 5.0.2系统. 2 现有环境 友善之臂tiny 4412开发板,superboot引导启动android5.0.2系统,内核版本为linux3.0.8. ...

  4. tiny4412开发板Android篇_2基于tiny4412的Android系统的编译

    tiny4412开发板使用的android系统版本是android5.0.2,由友善之臂公司提供,下载网址: https://pan.baidu.com/s/1pnn6N8Wpsx8PISLmKT59 ...

  5. tiny4412搭建linux开发环境,[Tiny4412] 移植 Linux4.4 到 Tiny4412 开发板上

    一. 前言 一直以来都是基于 Linux-2.6 内核学习嵌入式,但是工作之后发现,主流的 kernel 早已经不再使用 platform device 结构去描述设备信息了,而是换成了更为简洁的 d ...

  6. tiny4412开发板时钟操作示例

    在上一节总我们介绍了<Exynos4412芯片的时钟管理单元>,有了上一节的基础知识我们就可以写程序操作CPU的时钟了.通过操作led来感受时钟速率的变化.本文总共有三个示例,第一个是写一 ...

  7. Tiny4412开发板 LED灯的控制

    exynos4412裸版控制led灯. 1.查看电路图. 2.查看4412手册.找到对应的寄存器. 3.配置对应的寄存器. 一.再开发板上我们的外部设备led,在核心板.所以我们需要打开核心板电路图. ...

  8. 移植QT到tiny4412开发板

    目录 (一) 环境准备 (二) Qt源代码下载 (三) 移植tslib库 (四)操作流程 1.解压qt源码包 2.配置编译环境 3.生成Makefile 4.编译安装 5.安装一些库用来支持 qt 6 ...

  9. 漫谈LiteOS之开发板-GPIO(基于GD32450i-EVAL)

    [摘要] 本文主要从GPIO的定义.工作模式.特色.工作场合.以及GD32450i-EVAL开发板的引脚.对应的寄存器以及GPIO的流水灯示例对GPIO加以介绍,希望对你有所帮助. 1定义 GPIO( ...

最新文章

  1. IDEA报错总结:修改Java编译版本--maven项目
  2. python是全栈_Python全栈之路-3-字符串
  3. 推荐一个博客,或许给技术流的自己一些启示
  4. WebBrowser的Cookie操作(与CookieContainer的关系)
  5. ECharts.js学习(二)动态数据绑定
  6. mysql 加减乘除取小数点_mysql加减乘除
  7. c语言开发移动通信,基于ARM的高效C语言编程
  8. 【洛谷1640】[SCOI2010]连续攻击游戏
  9. maven的pom文件解析及配置
  10. popupwindow使用之异常:unable to add window -- token null is not valid
  11. Linux中select函数学习及实例笔记
  12. CMU 15-213 Introduction to Computer Systems学习笔记(9) Program Optimization
  13. [计算机通信网络]用例题来学会手算子网地址和子网掩码
  14. 软件测试课程设计——智云云盘
  15. [CF538H]Summer Dichotomy
  16. c语音大小写字母转换
  17. 基于Javaweb实现的人脸识别考勤系统
  18. 神经网络——Conv2d的使用
  19. Android HttpURLConnection下载网络图片,设置系统壁纸
  20. 区块链食品安全(区块链食品安全溯源系统痛点)

热门文章

  1. Ubuntu 18.04.x LTS及以上版本设置链路聚合网络
  2. [附源码]计算机毕业设计基于Springboot校园订餐管理系统
  3. java八种基础数据结构_8种常见数据结构及其Javascript实现
  4. Linux ARM平台开发系列讲解(入门篇) 1.1.3 开发板、Ubuntu和windows三者相互连接,无需路由器,全网最详细
  5. JAVA代码添加License
  6. 对软件测试团队“核心价值”的深度思考
  7. C++数据结构之哈希表
  8. 16.深入浅出:电压比较器——参考《模拟电子技术基础》清华大学华成英主讲
  9. 海康摄像头直播视频上传到流媒体服务器平台后如何降低延迟?(附TCP及UDP区别介绍)
  10. 介绍一个自制PLC的论坛