get_jiffies_64() : 현재 시간을 가져오는 함수
kerneltimer_func : 커널이 호출한다
Timer는 MCU에서 아주 중요한 것이다
짧은 시간, 인터럽트 등을 다룬다
문맥 교환을 하기 위해 사용하는 시간도 HZ를 사용한다
CPU에 따라 HZ를 다르게 설정한다
우리 우분투는 1초에 100번이므로 주기는 10ms이다
Pi의 top 명령어를 쳤을 때 나오는 시간은 타이머를 이용한 것
jiffies_64는 딜레이를 10ms이상 가능하기 때문에
더 짧은 것은 mdelay, udelay, ndelay 등을 사용한다
#define DELAY TIME MSEC (3*HZ /10) // 30
u64 endtime = get_jiffies_64() + DELAY_TIME_MSEC;
while(get_jiffies_64() < endtime) ;
위와 같이 사용해서 긴 딜레이를 처리할 수 있다
struct timeval : 초, ms를 매개변수로 가지는 시간 처리를 위한 구조체
struct timespec : 초, ns를 매개변수로 가지는 시간 처리를 위한 구조체
1970년 1월 1일 0시 0분부터
do_gettimeofday(): 시스템 시간을 초로 얻어온다
do_settimeofday(): 초로 환산된 시스템 시간을 설정한다
타이머에 관련된 실습
~/pi_bsp/drivers$ cp /mnt/lect_nfs/drivers/localtime.c .
~/pi_bsp/drivers$ gcc localtime.c -o localtime
~/pi_bsp/drivers$ ./localtime
구조체 tm에서 원하는 내용을 가져오는 모습이다
정확한 간격마다 일을 시키도록 하면 타이머 인터럽트를 이용해야한다
timer_list라는 구조체를 확인한다
expires 만료시간 값
Nullptr가 아닌지 판별하는 부분
코드가 길지 않으니 직접 만들어 본다
~/pi_bsp/drivers$ cp -r p238_ledkey p369_ledkey_int ; cd p369_ledkey_int
app.c에서 usleep을 주석처리한다
make 실행 후
pi에서
/mnt/ubuntu_nfs $ sudo mknod /dev/ledkey c 230 0
/mnt/ubuntu_nfs $ sudo insmod ledkey_dev.ko
/mnt/ubuntu_nfs $ sudo chmod 666 /dev/ledkey
/mnt/ubuntu_nfs $ ./ledkey_app 0x0f
스위치가 잘 먹는다
하지만 top을 했을 때 cpu 사용율이 100퍼이다
0퍼로 낮추기 위해 폴링이 아닌 인터럽트를 이용하고 블로킹io로 재울 것이다
app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#define DEVICE_FILENAME "/dev/ledkey_dev"
void print_OX(unsigned char);
int main(int argc,char * argv[])
{
char buff = 0;
char oldBuff = 0;
int dev;
int ret;
unsigned long val;
if(argc < 2)
{
printf("Usage : %s ledValue[0x00~0xff]\n",argv[0]);
return 1;
}
val = strtoul(argv[1],0,16);
if(val<0 || 0xff<val)
{
printf("Usage : %s ledValue[0x00~0xff]\n",argv[0]);
return 2;
}
buff=val;
dev = open( DEVICE_FILENAME, O_RDWR|O_NDELAY );
if(dev<0)
{
perror("open()");
return 2;
}
ret = write(dev,&buff,sizeof(buff));
if(ret < 0)
{
perror("write()");
return 3;
}
buff = 0;
do {
read(dev,&buff,sizeof(buff));
if(oldBuff != buff)
{
if(buff != 0)
{
printf("key : %d\n",buff);
if(buff == 8) //key:8
break;
print_OX(buff);
write(dev,&buff,sizeof(buff));
}
oldBuff = buff;
}
} while(1);
close(dev);
return 0;
}
void print_OX(unsigned char led)
{
int i;
led = 1 << led-1;
puts("1:2:3:4:5:6:7:8");
for(i=0;i<=7;i++)
{
if(led & (0x01 << i))
putchar('O');
else
putchar('X');
if(i < 7 )
putchar(':');
else
putchar('\n');
}
return;
}
dev.c
인터럽트 부분은 insmod할 때 init, rmmod할 때 exit 하게 한다
이곳이 핵심이다
init을 key > irq 순서였으면 free는 irq > key 순서로 해야한다
평상시 OFF일 때 0이다가 스위치를 눌러 ON이 되면 1이 된다
이것이 라이징 엣지이다
스위치가 눌리는 시점에 intrrupt가 발생
반대로 평상시 ON일 때 1이다가 스위치 누르면 0이된다
이것이 폴링 엣지이다
cat /proc/interrupts로 확인 가능
request부분 넣어서 최종 코드는
dev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#define DEBUG 1
#define LEDKEY_DEV_NAME "ledkey_dev"
#define LEDKEY_DEV_MAJOR 230
#define OFF 0
#define ON 1
#define GPIOLEDCNT 8
#define GPIOKEYCNT 8
static int gpioLed[GPIOLEDCNT] = {6,7,8,9,10,11,12,13};
static int gpioKey[GPIOKEYCNT] = {16,17,18,19,20,21,22,23};
static int key_irq[8] = {0};
static int keyNumber = 0;
static int gpioLedInit(void);
static void gpioLedSet(long);
static void gpioLedFree(void);
static int gpioKeyInit(void);
static int gpioKeyGet(void);
static void gpioKeyFree(void);
static int gpioKeyIrqInit(void);
static void gpioKeyIrqFree(void);
irqreturn_t key_isr(int irq, void *data)
{
int i;
for(i=0;i<GPIOKEYCNT;i++)
{
if(irq == key_irq[i])
{
keyNumber = i + 1;
break;
}
}
#if DEBUG
printk("key_isr() irq : %d, keyNumber : %d\n", irq, keyNumber);
#endif
return IRQ_HANDLED;
}
static int gpioLedInit(void)
{
int i;
int ret=0;
char gpioName[10];
for(i=0;i<GPIOLEDCNT;i++)
{
sprintf(gpioName,"led%d",i);
ret = gpio_request(gpioLed[i],gpioName);
if(ret < 0) {
printk("Failed gpio_request() gpio%d error \n",i);
return ret;
}
ret = gpio_direction_output(gpioLed[i],OFF);
if(ret < 0) {
printk("Failed gpio_direction_output() gpio%d error \n",i);
return ret;
}
}
return ret;
}
static void gpioLedSet(long val)
{
int i;
for(i=0;i<GPIOLEDCNT;i++)
{
gpio_set_value(gpioLed[i],(val>>i) & 0x1);
}
}
static void gpioLedFree(void)
{
int i;
for(i=0;i<GPIOLEDCNT;i++)
{
gpio_free(gpioLed[i]);
}
}
static int gpioKeyInit(void)
{
int i;
int ret=0;
char gpioName[10];
for(i=0;i<GPIOKEYCNT;i++)
{
sprintf(gpioName,"key%d",gpioKey[i]);
ret = gpio_request(gpioKey[i], gpioName);
if(ret < 0) {
printk("Failed Request gpio%d error\n", 6);
return ret;
}
}
for(i=0;i<GPIOKEYCNT;i++)
{
ret = gpio_direction_input(gpioKey[i]);
if(ret < 0) {
printk("Failed direction_output gpio%d error\n", 6);
return ret;
}
}
return ret;
}
static int gpioKeyGet(void)
{
int i;
int ret;
int keyData=0;
for(i=0;i<GPIOKEYCNT;i++)
{
// ret=gpio_get_value(gpioKey[i]) << i;
// keyData |= ret;
ret=gpio_get_value(gpioKey[i]);
keyData = keyData | ( ret << i );
}
return keyData;
}
static void gpioKeyFree(void)
{
int i;
for(i=0;i<GPIOKEYCNT;i++)
{
gpio_free(gpioKey[i]);
}
}
static int gpioKeyIrqInit(void)
{
int i;
for(i=0;i<GPIOKEYCNT;i++)
{
key_irq[i] = gpio_to_irq(gpioKey[i]);
if(key_irq[i] < 0)
{
printk("gpioKeyIrq() Failed gpio %d\n",gpioKey[i]);
return key_irq[i];
}
}
return 0;
}
static void gpioKeyIrqFree(void)
{
int i;
for(i=0;i<GPIOKEYCNT;i++)
{
free_irq(key_irq[i],NULL);
}
}
int ledkey_open (struct inode *inode, struct file *filp)
{
int num0 = MAJOR(inode->i_rdev);
int num1 = MINOR(inode->i_rdev);
printk( "ledkey open -> major : %d\n", num0 );
printk( "ledkey open -> minor : %d\n", num1 );
try_module_get(THIS_MODULE);
return 0;
}
ssize_t ledkey_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
// int result;
char kbuf;
printk( "ledkey read -> buf : %08X, count : %08X \n", (unsigned int)buf, count );
kbuf = (char)gpioKeyGet();
put_user(kbuf,buf);
// result = copy_to_user(buf, &kbuf, count);
return count;
}
ssize_t ledkey_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
// int i;
// int result;
char kbuff;
/* char kbuff[10];
for(i=0;i<count;i++)
get_user(kbuff[i],buf++);
*/
/* char kbuff[10];
copy_from_user(kbuff,buf,count);
*/
printk( "ledkey write -> buf : %08X, count : %08X \n", (unsigned int)buf, count );
get_user(kbuff,buf);
// result = copy_from_user(&kbuff,buf,count);
gpioLedSet(kbuff);
return count;
}
static long ledkey_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
printk( "ledkey ioctl -> cmd : %08X, arg : %08X \n", cmd, (unsigned int)arg );
return 0x53;
}
int ledkey_release (struct inode *inode, struct file *filp)
{
printk( "ledkey release \n" );
module_put(THIS_MODULE);
return 0;
}
struct file_operations ledkey_fops =
{
// .owner = THIS_MODULE,
.open = ledkey_open,
.read = ledkey_read,
.write = ledkey_write,
.unlocked_ioctl = ledkey_ioctl,
.release = ledkey_release,
};
int ledkey_init(void)
{
printk("ledkey ledkey_init \n");
int i, result=0;
char * irqName[8] = {"IrqKey0", "IrqKey1", "IrqKey2", "IrqKey3", "IrqKey4", "IrqKey5", "IrqKey6", "IrqKey7"};
gpioLedInit();
if(result < 0)
return result;
result=gpioKeyInit();
if(result < 0)
return result;
result=gpioKeyIrqInit();
if(result < 0)
return result;
for(i=0;i<GPIOKEYCNT;i++)
{
result = request_irq(key_irq[i], key_isr, IRQF_TRIGGER_RISING, irqName[i], NULL);
if(result < 0)
{
printk("request_irq() Failed irq %d\n", key_irq[i]);
}
}
result=register_chrdev( LEDKEY_DEV_MAJOR, LEDKEY_DEV_NAME, &ledkey_fops);
if (result < 0)
return result;
return 0;
}
void ledkey_exit(void)
{
printk( "ledkey ledkey_exit \n" );
unregister_chrdev( LEDKEY_DEV_MAJOR, LEDKEY_DEV_NAME );
gpioKeyIrqFree();
gpioKeyFree();
gpioLedFree();
}
module_init(ledkey_init);
module_exit(ledkey_exit);
MODULE_LICENSE("Dual BSD/GPL");
request 부분인
여기와
init 부분인
여기가 핵심 부분이다
그리고 4번 버튼 누르면 자꾸 꺼지니까(8로 인식해서)
read 부분을 이렇게 바꿔준다
0~255의 값이 아닌 1~8의 값을 리턴하는 것
등록 - index와 함수 등록
호출 - 등록된 함수 호출
제거 - free로 반납하기
지금까지 NULL을 쓰고 전역변수로 사용했다
이제는 전역변수를 매개변수로 바꿔 데이터 공유하기를 해볼 것이다
~/pi_bsp/drivers$ cp -r p369_ledkey_int p369_ledkey_int_kmalloc
폴더를 복사해준다
key_isr 함수는 커널이 호출하는 것이다
22 static int key_irq[8] = {0};
23 static int keyNumber = 0;
이 두개를 바꿀 것이다
key_irq는 4군데에서 사용 중
둘이 합쳐서 36바이트이고 구조체로 선언할 것이다
filp를 쓸 것이다
근데 init이랑 exit 함수가 void 타입이라 공유할 수가 없다
어떤 프로세스가 열지 모르기 때문에 안된다
(insmod만으로는 다음에 뭘 할지 모름)
동적메모리할당+구조체인터럽트루트 사용을 open/close에서 할 것이다
open을 수정한다
힙 영역의 주소를 저장
filp->private_data = pKeyData;
추가해준다
그리고 init, exit에 있는 init, free 부분을 open, release에 각각 가져온다
그리고 구조체는 static으로 하면 안되고 변수 초기화도 하지 않는다
gpioKeyIrqInit의 매개변수로 void였는데,
keyData * pKeyData로 수정하고 사용하는 부분에도 pKeyData를 넣어준다
"*(변수).멤버"로 간접멤버접근을 했는데 너무 기니까 "(변수)->멤버"로 사용한다
동적메모리 할당하고 init 및 free 내용 변경
key_isr
ledkey_open
ledkey_release
위와 같이 수정한다
이제 다 완료하면
typedef struct {
int key_irq[8];
int keyNumber;
} keyData;
구조체로 선언하고
static int gpioKeyIrqInit(keyData * pKeyData);
static void gpioKeyIrqFree(keyData * pKeyData);
매개변수를 void에서 바꿔주고(아래 함수도 마찬가지)
irqreturn_t key_isr(int irq, void *data)
{
int i;
keyData * pKeyData = (keyData *)data;
for(i=0;i<GPIOKEYCNT;i++)
{
if(irq == pKeyData->key_irq[i])
{
pKeyData->keyNumber = i+1;
break;
}
}
#if DEBUG
printk("key_isr() irq : %d, KeyNumber : %d\n",irq, pKeyData->keyNumber);
#endif
return IRQ_HANDLED;
}
변수에 간접접근하게 다 바꿔준 뒤
static int gpioKeyIrqInit(keyData * pKeyData)
{
int i;
int result;
char * irqName[8] = {"IrqKey0","IrqKey1","IrqKey2","IrqKey3","IrqKey4","IrqKey5","IrqKey6","IrqKey7"};
for(i=0;i<GPIOKEYCNT;i++)
{
pKeyData->key_irq[i] = gpio_to_irq(gpioKey[i]);
if(pKeyData->key_irq[i] < 0)
{
printk("gpioKeyIrq() Failed gpio %d\n", gpioKey[i]);
return pKeyData->key_irq[i];
}
}
for(i=0;i<GPIOKEYCNT;i++)
{
result = request_irq(pKeyData->key_irq[i],key_isr,IRQF_TRIGGER_RISING,irqName[i],pKeyData);
if(result < 0)
{
printk("request_irq() failed irq %d\n",pKeyData->key_irq[i]);
return result;
}
}
return 0;
}
init 부분도 바꿔주고
static void gpioKeyIrqFree(keyData * pKeyData)
{
int i;
for(i=0;i<GPIOKEYCNT;i++)
{
free_irq(pKeyData->key_irq[i],pKeyData);
}
}
free 부분도 바꿔주고
static int ledkey_open (struct inode *inode, struct file *filp)
{
int result;
int num0 = MAJOR(inode->i_rdev);
int num1 = MINOR(inode->i_rdev);
keyData * pKeyData = (keyData *)kmalloc(sizeof(pKeyData), GFP_KERNEL); //36바이트를 가리키는 포인터변수
if(!pKeyData)
return -ENOMEM;
printk( "ledkey open -> major : %d\n", num0 );
printk( "ledkey open -> minor : %d\n", num1 );
try_module_get(THIS_MODULE);
result=gpioLedInit();
if(result < 0)
return result;
result=gpioKeyInit();
if(result < 0)
return result;
result = gpioKeyIrqInit(pKeyData);
if(result < 0)
return result;
filp->private_data = pKeyData;
return 0;
}
open 내용도 바꿔주고
ssize_t ledkey_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
// int result;
char kbuf;
keyData * pKeyData = (keyData *)filp->private_data;
#if DEBUG
printk( "ledkey read -> buf : %08X, count : %08X \n", (unsigned int)buf, count );
#endif
// kbuf = (char)gpioKeyGet();
kbuf = pKeyData->keyNumber;
put_user(kbuf,buf);
if(pKeyData->keyNumber) { pKeyData->keyNumber = 0; }
// result = copy_to_user(buf, &kbuf, count);
return count;
}
read 부분 수정
int ledkey_release (struct inode *inode, struct file *filp)
{
keyData * pKeyData = (keyData *)filp->private_data;
printk( "ledkey release \n" );
module_put(THIS_MODULE);
gpioKeyIrqFree(pKeyData);
gpioKeyFree();
gpioLedFree();
return 0;
}
release 부분 수정
int ledkey_init(void)
{
int result;
printk( "ledkey ledkey_init \n" );
result = register_chrdev( LEDKEY_DEV_MAJOR, LEDKEY_DEV_NAME, &ledkey_fops);
if (result < 0) return result;
return 0;
}
void ledkey_exit(void)
{
printk( "ledkey ledkey_exit \n" );
unregister_chrdev( LEDKEY_DEV_MAJOR, LEDKEY_DEV_NAME );
}
나머지 아래 부분은 위와 같이 다 빼준 상태이다
이렇게 설정하고 각각 printk 부분을 if-endif로 막아주고 make한 뒤 실행한다
최초에는 안되다가 인터럽트가 발생하면 정상 작동한다
마지막으로 release에서
해당 내용을 넣어서 free해주면 완료된다
동적할당
정적으로 할당하면 임베디드에서 메모리가 부족하기 때문에