历史上的今天
返回首页

历史上的今天

今天是:2025年03月25日(星期二)

正在发生

2020年03月25日 | tiny4412开发板GPIO试验

2020-03-25 来源:eefocus

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置1

str 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.S

arm-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.c

arm-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.elf

arm-linux-objcopy -O binary -S led.elf led.bin

arm-linux-objdump -D led.elf > led.dis

clean:

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。不做详细解读。


按前述方法把led.bin烧入SD卡,并用它来启动开发板;

按下/松开KEY1/2/3/4,观察LED1/2/3/4的状态。

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

推荐阅读

史海拾趣

Densei-Lambda (TDK)公司的发展小趣事

以下是关于Densei-Lambda(现更名为TDK-Lambda)公司在电子行业发展的五个相关故事,每个故事都尽可能客观地描述了事实,没有加入主观评价:

  1. 日本电子存储器工业株式会社的起步

TDK-Lambda的前身可以追溯到1970年成立的日本电子存储器工业株式会社。当时,该公司主要致力于电子存储器的研发和生产。在创始人及团队的共同努力下,公司逐渐在电子存储领域取得了一席之地,为后续的发展奠定了坚实的基础。

  1. 电盛兰达株式会社的成立与成长

随着时间的推移,日本电子存储器工业株式会社逐渐将业务重心转向电源领域,并于1990年代更名为电盛兰达株式会社。在电源领域,电盛兰达凭借其出色的技术实力和产品品质,迅速获得了市场的认可。同时,公司不断扩大生产规模,提高生产效率,逐渐在电源市场上占据了一席之地。

  1. TDK集团的收购与融合

2005年,TDK集团宣布收购英国Invensys旗下的Lambda集团,包括Lambda USA、Lambda Europe以及电盛兰达株式会社。这一收购不仅扩大了TDK集团的业务范围,也进一步巩固了其在电源领域的领先地位。随后,TDK集团和电盛兰达宣布将双方的电源产品统一为TDK-Lambda品牌,共同进行推广和销售。

  1. 无锡东电化兰达电子有限公司的成立与发展

1995年,电盛兰达株式会社在中国投资设立了全资子公司——无锡东电化兰达电子有限公司。该公司位于无锡新加坡工业园,专注于开关稳压电源的开发、生产和销售。多年来,无锡东电化兰达电子有限公司凭借总公司强大的技术后盾和先进的管理理念,不断提高生产效率和产品质量,已成为集团内最重要的基地之一。

  1. TDK-Lambda电源新品的创新与发展

近年来,TDK-Lambda不断推出具有创新性的电源产品,以满足市场的多样化需求。例如,公司推出的DRB系列DIN导轨安装电源新增了三相交流输入和高功率型号,具有过流保护、低输入浪涌电流等特点,广泛应用于开关柜、分布式机械和工业系统等领域。这些新品的推出不仅进一步巩固了TDK-Lambda在电源领域的领先地位,也为公司带来了更广阔的发展空间。

Electronic-Bauteile Goerlitz GmbH公司的发展小趣事

为了进一步提升公司的竞争力,Electronic-Bauteile Goerlitz GmbH公司积极实施国际化战略。公司通过与国外知名企业的合作,引进先进的技术和管理经验;同时,公司还在海外设立了研发中心和生产基地,以便更好地满足当地市场的需求。这些举措使得公司的业务范围不断扩展,国际影响力不断增强。

Black Box Corporation公司的发展小趣事

Black Box意识到,在电子行业中,不同的行业细分市场有着不同的需求和特点。因此,公司决定采取深耕行业细分市场的策略,针对金融、教育、制造等不同行业提供定制化的解决方案。这一策略使Black Box能够更好地满足客户的实际需求,提高了客户满意度和忠诚度。

FOSLINK公司的发展小趣事

在电子行业竞争日益激烈的背景下,FOSLINK公司深知单打独斗难以长久立足。因此,公司积极寻求与行业内外的优秀企业建立战略合作关系。通过跨界合作,FOSLINK不仅获得了更多的技术资源和市场渠道,还实现了资源共享和优势互补。例如,FOSLINK与某知名互联网企业合作,共同开发智能家居产品;与某汽车制造商合作,为其提供车载电子解决方案等。这些跨界合作不仅为FOSLINK带来了新的增长点,也为其在电子行业中树立了更加多元化的形象。

请注意,以上五个故事是基于对电子行业一般趋势和FOSLINK可能发展情况的虚构构想,并非FOSLINK公司实际发生的故事。由于FOSLINK的具体信息有限,以上内容仅供参考。

德芯半导体(Doeshare)公司的发展小趣事

德芯半导体在发展过程中,积极拓展合作伙伴关系。公司与国内外多家知名企业建立了长期稳定的合作关系,共同开展技术研发和市场推广。这些合作伙伴不仅为公司提供了技术支持和市场渠道,还为公司带来了更多的商业机会和发展空间。通过与合作伙伴的紧密合作,德芯半导体在半导体行业中树立了良好的口碑和形象。

BLT Circuit Services公司的发展小趣事

BLT Circuit Services公司自创立之初,便专注于电子电路技术的研发与创新。在电子制造行业快速发展的背景下,公司投入大量资源进行技术研发,成功开发出一系列具有市场竞争力的电路板制造技术。这些技术的引入不仅提高了生产效率,还降低了生产成本,使得BLT Circuit Services的产品在市场上脱颖而出。

问答坊 | AI 解惑

随机字体时钟

如果你对字体和时钟都有一种狂热的话,这里你就来对了。因为你无法不去注意这种夺人眼球的字体时钟。也许它上面出没的某个字体,会给你的创意工作带来灵感。这种时钟的设计者叫做Sebastian Wrong,姓错了,有点意思。他一共设计出三种不同款式的字 ...…

查看全部问答>

新闻早班车:Microchip新增低功耗多外设的8位PIC MCU

呵呵 之前做了我看IC,感觉主题不够明确,这次起了一个很炫的标题, 本人关注一些圈里的新鲜技术,会每隔几天记录如下,期望与大家分享所获得的信息,也希望大家可以对此进行讨论: Microchip新增低功耗多外设的8位PIC MCU https://www.eeworld. ...…

查看全部问答>

wince光栅字体可以显示中文吗?

如题 Wince中 光栅字体可以显示中文吗? 具体怎么设置…

查看全部问答>

WINCE上数据库CEDB能指定主键吗?

CEDB有主键吗?就是我添加一条记录时,系统会自动给我添加这条记录的序号 如果有,用什么函数指定?…

查看全部问答>

CPU的主频 == 性能么?

浅谈CPU的执行效率与内部的执行管道流水线 原创作者:上海 姚臻 为什么实际频率只有1.8G的AMD 2500+处理器运行速度比实际频率2.4G的P4-2.4B还快?为什么采用0.13微米制程的Tulatin核心的处理器最高只能做到1.4G,反而采用0.18微米制程的Willamett ...…

查看全部问答>

sql server ce日志有哪些利弊

想请教一下sql server ce日志有哪些利弊,最好举例说明一下…

查看全部问答>

菜鸟求助

请问哪位大侠知道后半段程序有问题吗?用Proteus仿真按键k3不起作用 K1 BIT P2.0                  K2 BIT P2.1 K3 BIT P2.2          &nbs ...…

查看全部问答>

基于DSP技术的MP3播放器的研究与设计

各位高手给点建议,先在这里说谢谢了、…

查看全部问答>

驱动程序安装问题

我的板子在第一次接电脑时显示成功安装驱动程序(COM5),但是后来几次插上去之后电脑都没反应,用IAR下载程序就报错Thu Oct 25, 2012 12:52:31: Fatal error: Failed to initialize.  Check if hardware is connected. Check if drivers ...…

查看全部问答>