일반적인 Memory Address는 32bit를 사용한다.
0x0000 0000 ~ 0xFFFF FFFF 까지 각 1Byte 공간을 저장하여 총 4GByte 저장 공간 Address를 가짐
하지만, 라즈베리파이의 ARM에 LPAE 기능을 사용하여 35bit까지 표현하고 최대 메모리는 32GB까지 저장 가능
C언어에서 35bit까지 표기 불가능, Low Peripheral 메모리 주소를 사용하면 된다.

0x7C00 0000을 건드리고 싶다면, 0xFC00 0000을 건드리면 된다.

데이터시트를 살펴보면, Base 주소 : 0x7e20 0000이다. 따라서, 실제 라즈베리파이에서 건드려야 하는 주소는 0xFe20 0000이다.
참고) 임베디드에서 e는
에코라 읽는다. 2와 커뮤니케이션 혼동을 피하기 위해
offset : 얼마나 떨어져 있는지 표기

GPFSEL1 Register의 26:24에 001을 넣어야하고, 0xFE20 0004 주소 공간을 건드려야한다.

18번핀 SET : 0xFE20 001C의 18번 bit에다가 1 세팅 => LED ON
18번핀 Clear : 0xFE20 0028의 18번 bit에다가 1 세팅 => LED OFF
static : 해당 파일 내에서만 사용가능한 전역변수
nobrand.c 에 추가
#include <asm/io.h>
static volatile uint32_t *BASE;
static volatile uint32_t *GPFSEL1;
static volatile uint32_t *GPSET0;
static volatile uint32_t *GPCLR0;
static int nobrand_init(void) {
BASE = (uint32_t *)ioremap(0xFE200000, 256); // 해당 주소의 물리적 공간을 사용하기 위해
// offset을 해주는데, int의 자료형 크기가 4Byte이기 때문에 4를 나누어줘야 주소 공간 offset이 정상 작동된다.
GPFSEL1 = BASE + (0x04 / 4); // `0xFE20 0004`
GPSET0 = BASE + (0x1C / 4); // `0xFE20 001C`
GPCLR0 = BASE + (0x28 / 4); // `0xFE20 0028`주소
// GPFSEL1 Register의 26:24에 001을 넣는다.
*GPFSEL1 &= ~(0x7 << 24);
*GPFSEL1 |= (1 << 24);
*GPSET0 = (1 << 18); // LED ON : `0xFE20 001C`의 18번 bit에다가 1 세팅
if (register_chrdev(NOD_MAJOR, NOD_NAME, &fops) < 0) {
printk( KERN_INFO "ERROR!!! register error\n");
}
printk(KERN_INFO "hi\n");
return 0;
}
static void nobrand_exit(void) {
*GPCLR0 = (1 << 18); // LED OFF : `0xFE20 0028`의 18번 bit에다가 1 세팅
iounmap(BASE);
unregister_chrdev(NOD_MAJOR, NOD_NAME);
printk( KERN_INFO "BYE BYE\n\n");
}
버튼을 제어하기 위해서는 데이터 시트에서 다음 부분을 참고해야합니다.

app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main()
{
printf("OPEN FILE!!\n");
int fd = open("/dev/nobrand", O_RDWR);
if (fd == -1) {
printf("FILE OPEN ERROR!!!\n");
return 0;
}
//read
char buf[100];
while(1) {
read(fd, buf, 2); // 커널에서 buf값 읽어오기
if (buf[0] == 'P') // 만약 buf[0]이 'P'이면 LED ON
{
ioctl(fd, 3, 0);
}
else // 만약 buf[0]이 'P'가 아니면 LED OFF
{
ioctl(fd, 4, 0);
}
usleep(100*1000);
}
printf("CLOSE FILE!!\n");
close(fd);
return 0;
}
nobrand.c
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#define NOD_NAME "nobrand"
#define NOD_MAJOR 100
MODULE_LICENSE("GPL");
static volatile uint32_t *BASE;
static volatile uint32_t *GPFSEL1;
static volatile uint32_t *GPSET0;
static volatile uint32_t *GPCLR0;
static volatile uint32_t *GPLEV0;
static volatile uint32_t *GPIO_PUP_PDN_CNTRL_REG0;
static void led(int isTurnOn);
static int isButtonPush(void);
static int nobrand_open(struct inode *inode, struct file *filp) {
printk( KERN_INFO "OPEN!!!\n");
return 0;
}
static int nobrand_release(struct inode *inode, struct file *filp) {
printk( KERN_INFO "CLOSE!!!\n");
return 0;
}
static ssize_t nobrand_read(struct file *filp, char *buf, size_t count, loff_t *ppos) {
if (isButtonPush()) buf[0] = 'R';
else buf[0] = 'P';
buf[1] = '\0';
return count;
}
static long nobrand_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
if (cmd == 3) {
led(1);
}
if (cmd == 4) {
led(0);
}
printk( KERN_ALERT "%d\n", cmd);
return 0;
}
static struct file_operations fops = {
.open = nobrand_open,
.release = nobrand_release,
.read = nobrand_read,
.unlocked_ioctl = nobrand_ioctl
};
static int nobrand_init(void) {
printk( KERN_INFO "OK HELLO NOBRAND!!\n");
if (register_chrdev(NOD_MAJOR, NOD_NAME, &fops) < 0) {
printk( KERN_INFO "ERROR!!! register error\n");
}
BASE = (uint32_t *)ioremap(0xFE200000, 256);
GPFSEL1 = BASE + (0x04 / 4);
GPSET0 = BASE + (0x1C / 4);
GPCLR0 = BASE + (0x28 / 4);
GPLEV0 = BASE + (0x34 / 4);
GPIO_PUP_PDN_CNTRL_REG0 = BASE + (0xE4 / 4);
//24~26bit 001 output
*GPFSEL1 &= ~(0x7 << 21); //000
*GPFSEL1 |= (1 << 21); //001
//using embedded pull up : 데이터 시트에서 초기값이 01이라 다음 코드 안해줘도 되긴 한다.
*GPIO_PUP_PDN_CNTRL_REG0 &= ~(0x3 << 4);
*GPIO_PUP_PDN_CNTRL_REG0 |= (0x1 << 4);
return 0;
}
static void nobrand_exit(void) {
unregister_chrdev(NOD_MAJOR, NOD_NAME);
led(OFF);
iounmap(BASE);
printk( KERN_INFO "BYE BYE\n\n");
}
static int isButtonPush(void) {
int ret = 0;
ret = (*GPLEV0 >> 2) & 0x1; // 버튼 2번핀에 연결되어 있다.
printk(KERN_INFO "BTN : %d\n", ret);
return ret;
}
static void led(int isTurnOn) {
if (isTurnOn) // LED ON
{
*GPSET0 = (1 << 18);
}
else // LED OFF
{
*GPCLR0 = (1 << 18);
}
}
module_init(nobrand_init);
module_exit(nobrand_exit);