일반적인 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);