● 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
==> 컨트롤 할 예정
● GPIO는 디바이스 드라이버 framework에 이미 존재
– 많이 사용하기 때문에 표준 인터페이스 존재
==> GPIO 17 LED ON
● 직접 메모리에 read/write로 I/O 제어 가능
● 실험을 위해 devmem을 통해 제어
– 매번 컴파일 번거로움 => devmem을 통해 1차 확인
– 기본 커널 직접 제어 불가능
● 커널 설정 수정 필요 // config
● 리눅스 shell에서 동작
– VA로 접근 (가상주소)
● 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
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");
● 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