GPIO接口开发

本文是在学习 麦子学院 的嵌入式课程之后整理的一篇关于GPIO的笔记。内容比较基础。不过用老师的话来讲,思路很重要。学习应该是要触类旁通、举一反三的。

实验环境

课程中老师使用的是 友善之臂的 s5pv210 开发板。而我手头是一块 s3c2440 开发板。本文的程序都是基于 s3c2440 编写。交叉工具链使用的是我自己制作的ARM-Linux-gcc工具链。可以参考这里. 另外烧录工具使用的是友善之臂提供的 minitools。

开发流程

本人的学习经历算是比较奇葩。学习过数字电路基础,但并未真正玩过一些单片机或者ARM开发板。后来又直接跳到了应用层面:网络编程,网络协议这些。所以对底层总是保持一颗好奇的心。想搞清楚在ARM开发板的层面上是如何点亮或者关闭一个LED灯。又或者如何操作网卡来接收发送数据。当然还有更底层的东西,比如信号处理这些,可是好奇心并不是很大。对我目前的学习而言,就是想把 硬件接口开发——Linux内核驱动开发—— Linux系统移植以及集成——上层应用开发 这一整套知识尽可能的掌握。好了,闲话不多说了,开始正题。

GPIO就是通用输入输出的意思。是计算机上最简单也是最常用的一种接口资源。在没有做这个实验之前,也能做猜想到电路LED灯也就是将板子上的某个引脚设置为地或者高电平就行了。下面是具体的过程。

1. 在 mini2440 开发板原理图,也就是电路图上找到 LED

GPIO接口开发_第1张图片

这里以 led1 led2 为例,它们是接在 GPB5 GPB6 这两个IO口上。

 

2. 在 s3c2440 芯片手册中查找相应的GPIO口

GPIO接口开发_第2张图片

 

GPIO接口开发_第3张图片

按照芯片手册的说明,GPB5 位于 GPBCON 控制寄存器的 [11,10]位,而GPB6位于[13,12]位。它们的功能描述也很清楚,01表述输出功能。再来看GPBDAT数据接口。GPB[10:0]一共11位,GPBDat[5]就代表GPB5的输出,GPBDat[6]就代表GPB6的输出。按照后面的描述,当GPIO口设置为 output 功能,pin state 跟相应的 bit 位的值是一致的。在写代码实现的过程中,要注意两个寄存器的地址GPBCON——0x56000010  GPBDAT——0x56000014.

主程序

参考上面的思路: 找到相应的寄存器地址——设置寄存器为输出功能——将IO口设置为低电平,点亮LED灯 。下面给出部分代码,完整的代码可以参考 麦子学院 老师的视频。手把手教你写代码,不怕你不会。真心很赞。

#ifndef __ASM_ARCH_GPIO_H
#define __ASM_ARCH_GPIO_H

struct s3c2440_gpio_bank
{
    unsigned int con;
    unsigned int dat;
    unsigned int up;
    unsigned char reserved[4];
};


struct s3c2440_gpio
{
    struct s3c2440_gpio_bank gpio_a;
    struct s3c2440_gpio_bank gpio_b;
    struct s3c2440_gpio_bank gpio_c;
    struct s3c2440_gpio_bank gpio_d;
    struct s3c2440_gpio_bank gpio_e;
    struct s3c2440_gpio_bank gpio_f;
    struct s3c2440_gpio_bank gpio_g;
    struct s3c2440_gpio_bank gpio_h;
};

#endif

 

#ifndef _S3C2440_CPU_H
#define _S3C2440_CPU_H

#define s3c2440_GPIO_BASE  0x56000000


#define __REG(x) (*(volatile unsigned int *)(x)) 

#define readb(a) (*(volatile unsigned char *)(a))
#define readw(a) (*(volatile unsigned short *)(a))
#define readl(a) (*(volatile unsigned int *)(a))


#define writeb(v, a) (*(volatile unsigned char *)(a) = v)
#define writew(v, a) (*(volatile unsigned short *)(a) = v)
#define writel(v, a) (*(volatile unsigned int *)(a) = v)

#endif

 

#include "gpio.h"
#include "led.h"
#include "cpu_io.h"



void led_init()
{
    struct s3c2440_gpio *base = (struct s3c2440_gpio *)s3c2440_GPIO_BASE;
    unsigned int var;

    var = __REG(&base->gpio_b.con);
    var &= ~(0xf << 10);
    var |= (0x5 << 10);
    writel(var, &base->gpio_b.con);
}


void led_blink(LEDStatus s)
{
    struct s3c2440_gpio *base = (struct s3c2440_gpio *)s3c2440_GPIO_BASE;
    unsigned int var;

    if (s == ON)
    {
        var = __REG(&base->gpio_b.dat);
        var &= ~(0x3 << 5);
        writel(var, &base->gpio_b.dat);
    }
    else
    {
        var = __REG(&base->gpio_b.dat);
        var |= (0x3 << 5);
        writel(var, &base->gpio_b.dat);
    }
}

 

下面是main函数调用相应的接口:

#include "gpio.h"
#include "led.h"
#include "cpu_io.h"


void delay();
void led_test();


int main()
{
    led_test(); 
    return 0;
}


void delay()
{
    volatile unsigned int i = 1000000;
    while (i--);
}

void led_test()
{
    led_init();
    LEDStatus s;
    
    while (1)
    {
        s = ON;
        led_blink(s);
        delay();
        s = OFF;
        led_blink(s);
        delay();
    }
}

 

最值得注意的是,在底层操作中每个 bit 都有重要的含义。所以在操作寄存器的时候不能一股脑往里面写一个值。通常的做法是 读取——操作——回写 的机制。其中操作的过程中使用 &= |= 只改写相应的位。不影响寄存器原来的值。

通用makefile

这一阶段的学习集中在ARM底层的一些接口开发。为将来更好的学习Linux内核驱动打基础。所以大都是C语言编写的裸板程序,并且这些程序组织关系非常相似。都是先通过 函数,然后在 跳到 main 函数,然后在 函数中调用相关的接口模块。所以可以使用统一的 makefile 来管理。下面是一个我在课程老师编写的基础上做了一些修改的 makefile 模板。 函数中调用相关的接口模块。所以可以使用统一的 makefile 来管理。下面是一个我在课程老师编写的基础上做了一些修改的 makefile 模板。

# this makefile is the temlate of 
# all ARM-Linux bare machine develop
# 
# Gru__ at 20160108



# the makefile var

CURDIR        :=        $(shell basename `pwd`)
BUILDDIR    ?=        ./build/
TARGET        :=        $(CURDIR).bin
BUILD        :=         $(TARGET).elf
SRCFILE        :=        $(shell ls | grep '\.'c$)
COBJS        +=        start.o
COBJS        +=        $(patsubst %.c, %.o, $(SRCFILE))


PATH += :/home/gru/cross/embedded-toolchains/tool-chain/bin
CROSS_COMPILE    :=    arm-linux-
CC            :=        $(CROSS_COMPILE)gcc
LD            :=        $(CROSS_COMPILE)ld
OBJCOPY            :=        $(CROSS_COMPILE)objcopy


BOOTWAY        ?=        RAM
CFLAGS        +=        -Wall
CFLAGS         +=         -I./inc
LDFLAGS        +=        -Tmap.lds

ifeq ($(BOOTWAY), RAM)
LDFLAGS        +=        -Ttext=0x30000000
else
LDFLAGS        +=        -Ttext=0x0
endif


# the common make way

all : $(TARGET)
    
ifeq ($(BOOTWAY), RAM)
$(TARGET) : $(BUILD)
    $(OBJCOPY) -O binary $^ $@
else

endif


$(BUILD) : $(COBJS)
    $(LD) $(LDFLAGS) -o $@ $^


%.o : %.c
    $(CC) $(CFLAGS) -c $^ -o $@


%.o : %.S
    $(CC) $(CFLAGS) -c $^ -o $@


clean:
    rm -rf *.bin *.elf *.o

 

每个小实验的目录组织关系如下:

你可能感兴趣的:(GPIO接口开发)