GPIO

● GPIO(General Purpose Input Output)
– 일반적인 용도의 입출력 포트
– input/ouput
● SoC의 하나의 입출력 핀
– GPIO + alternate functions
● 58 General-Purpose Input/Output (GPIO) 제공
– Bank 0: GPIOs 0 to 27
– Bank 1: GPIOs 28 to 45
– Bank 2: GPIOs 46 to 57

  • 풀업 , 풀다운 설정 가능
  • 디렉션 레지스터 : 방향을 설정 가능 (인풋 , 아웃풋)
  • Function 레지스터 : Function 혹은 on/off로 사용가능
  • 핀을 셋:1 or 클리어:0으로 만드는 레지스터
  • Rising Edge Detect 레지스터 : 인터럽트가 발생하여 1로 올라갈 때 그것을 감지
  • Falling Edge : 떨어질 때

==> 컨트롤 할 예정

  • Function 레지스터
  • Pin Output Set 레지스터 => 1로 세팅
  • pin output clear 레지스터 => 0으로 세팅
  • Pin Level : 현재의 핀 레벨을 읽는 레지스터
  • 그 외 : 인터럽트

GPIO 17로 LED 제어 해보기

  • 000 인풋
  • 001 : 아웃풋 (파형)
  • 100~010 : function
  • The GPIO has the following registers. All accesses are assumed to be 32-bit. The GPIO register base address is 0x7e200000.

  • 32개
  • set을 하면 3.3V

sysfs를 통한 GPIO 제어

● GPIO는 디바이스 드라이버 framework에 이미 존재
– 많이 사용하기 때문에 표준 인터페이스 존재

  • export에 echo로 입력 시 gpiochip에 node가 생김

  • direction : input or output
  • edge : falling or rising
  • value : high or low

==> GPIO 17 LED ON

Memory mapped I/O 통한 GPIO 제어

● 직접 메모리에 read/write로 I/O 제어 가능
● 실험을 위해 devmem을 통해 제어
– 매번 컴파일 번거로움 => devmem을 통해 1차 확인
– 기본 커널 직접 제어 불가능
● 커널 설정 수정 필요 // config
● 리눅스 shell에서 동작
– VA로 접근 (가상주소)

  • 7e200000이 PA, fe200000은 VA다.

  • vi/boot/cmdline.txt iomem = relaxed

  • 0x04 : offset 주소

  • 핀설정 필요

● ALT func 체크
● devmem 0xfe200004 (4번)
0x00024000 => 23:21 비트 => 0000(input) => GPIO 설정
● Output 설정
– 23:21 비트 설정 => 0001(ouput) 0x224000
– devmem 0xfe200004 32 0x224000 // 32bit 레지스터로 세팅, output으로 해야하므로 0x224000 // direction 설정

GPSET0 - 세팅만 하는 레지스터

● ALT func 체크
● devmem 0xfe20001C
0x6770696F => 17번 비트 0
● devmem 0xfe20001C 32 0x6772696F
– 17번 비트 셋팅
==> LED ON

  • 28 : 클리어
  • 34 : 레벨 확인 / 값을 확인 가능

GPIO 디바이스 드라이버

  • int gpio_direction_output(unsigned gpio, int value) // value = 0 or 1

  • int gpio_direction_input(unsigned gpio); // gpio number

  • int gpio_request(unsigned gpio, const char *label) // open을 해서 다른곳에서 못쓰게 label

  • void gpio_free(unsigned gpio) // 다 쓰고나면 free

  • void gpio_set_value_cansleep(unsigned gpio, int value) // set value

  • void gpio_set_value(unsigned gpio, int value) // get value

  • int gpio_get_value_cansleep(unsigned gpio)

  • int gpio_get_value(unsigned gpio)

코드

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>

#define BUF_SIZE 1024

static char kernel_write_buffer[BUF_SIZE];
static dev_t kdt_dev;
static struct class *kdt_class;
static struct cdev kdt_device;

#define DRIVER_NAME "kdt_gpio_driver"
#define DRIVER_CLASS "kdt_gpio_class"

#define KDT_GPIO_OUTPUT 17
#define KDT_GPIO_INPUT 16

static ssize_t kdt_driver_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
{
        if (copy_to_user(buf, "t", 1)) {
                pr_err("read: error!\n");
        }

        pr_info("Value of button: %d\n", gpio_get_value(KDT_GPIO_INPUT));

        return BUF_SIZE;
}

ssize_t kdt_driver_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset)
{
        if (copy_from_user(kernel_write_buffer, buf, count)) {
                pr_err("write: error\n");
        }

        switch (kernel_write_buffer[0]) {
                case 0:
                        pr_info("gpio %d: low\n", KDT_GPIO_OUTPUT);
                        gpio_set_value(KDT_GPIO_OUTPUT, 0);
                        break;
                case 1:
                        pr_info("gpio %d: high\n", KDT_GPIO_OUTPUT);
                        gpio_set_value(KDT_GPIO_OUTPUT, 1);
                        break;
                default:
                        pr_info("Invalid Input!\n");
                        break;
        }

        pr_info("write: done\n");

        return count;
}

static int kdt_driver_open(struct inode *device_file, struct file *instance)
{
        pr_info("open\n");
        return 0;
}

static int kdt_driver_close(struct inode *device_file, struct file *instance)
{
        pr_info("close\n");
        return 0;
}

static struct file_operations fops = {
        .owner = THIS_MODULE,
        .open = kdt_driver_open,
        .release = kdt_driver_close,
        .read = kdt_driver_read,
        .write = kdt_driver_write
};

static int __init kdt_module_init(void)
{
        /* 여기서 노드를 할당 받는다. */
        if (alloc_chrdev_region(&kdt_dev, 0, 1, DRIVER_NAME) < 0) {
                pr_info("Device Nr. could not be allocated!\n");
                return -1;
        }

        pr_info("할당 받은 Major = %d Minor = %d \n", MAJOR(kdt_dev), MINOR(kdt_dev));

        /* device class 생성 */
        if ((kdt_class = class_create(THIS_MODULE, DRIVER_CLASS)) == NULL) {
                pr_info("Device class can not be created!\n");
                goto cerror;
        }

        /* device file 생성 */
        if (device_create(kdt_class, NULL, kdt_dev, NULL, DRIVER_NAME) == NULL) {
                pr_info("Can not create device file!\n");
                goto device_error;
        }

        /* character device file 초기화 */
        cdev_init(&kdt_device, &fops);

        /* 커널에 등록 */
        if (cdev_add(&kdt_device, kdt_dev, 1) == -1) {
                pr_info("Registering of device to kernel failed!\n");
                goto reg_error;
        }

        if (gpio_request(KDT_GPIO_OUTPUT, "kdt-gpio-17")) {
                pr_info("Can not allocate GPIO 17\n");
                goto reg_error;
        }

        if(gpio_direction_output(KDT_GPIO_OUTPUT, 0)) {
                pr_info("Can not set GPIO 16 to output!\n");
                goto gpio_17_error;
        }

        if(gpio_request(KDT_GPIO_INPUT, "kdt-gpio-16")) {
                pr_info("Can not allocate GPIO 16\n");
                goto gpio_17_error;
        }

        if(gpio_direction_input(KDT_GPIO_INPUT)) {
                pr_info("Can not set GPIO 16 to input!\n");
                goto gpio_16_error;
        }

        return 0;

gpio_16_error:
        gpio_free(16);
gpio_17_error:
        gpio_free(17);
reg_error:
        device_destroy(kdt_class, kdt_dev);
device_error:
        class_destroy(kdt_class);
cerror:
        unregister_chrdev_region(kdt_dev, 1);
        return -1;
}

static void __exit kdt_module_exit(void)
{
        gpio_set_value(16, 0);
        gpio_free(16);
        gpio_free(17);
        cdev_del(&kdt_device);
        device_destroy(kdt_class, kdt_dev);
        class_destroy(kdt_class);
        unregister_chrdev_region(kdt_dev, 1);
        pr_info("exit\n");
}

module_init(kdt_module_init);
module_exit(kdt_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("KTD <ktd@kdt.com>");
MODULE_DESCRIPTION("kdt character");
MODULE_VERSION("1.0.0");

  • 1 : LED ON , 0 : LED OFF

핀 설정

  • bootloader에서 1차 설정
  • Kernel device tree에서 2차로 설정

● Debugfs를 통해 확인
– sudo mount -t debugfs none /sys/kernel/debug


/sys/kernel/debug/pinctrl

– cat pinctrl-handles

– cat pinctrl-maps

– cat fe200000.gpio-pinctrl-bcm2711/pinmux-pins

사진 출처 : 수업 자료 및 BCM2711 Datasheet

0개의 댓글

관련 채용 정보