鋪墊:
這倆天寫了一個控制LED燈的驅動,真的是哭死我這個小白了,因為之前ARM上弄過相似的,以為很簡單,哪知道,還是有點麻煩的,現在的的程式是基于MIPS平台編寫的。什麼都得純手工。
正題:
LED燈驅動
在mips平台上,制作一個簡易的LED驅動來練習char字元驅動
- Step1:檢視對應硬體寄存器配置需求
首先檢視,目前LED對應的端口是PB03,查找資料手冊,GPIO部分,看看需要如何設定,可以看到下面2個主要的寄存器,一個控制方向(輸入or輸出),一個資料寄存器(寫入0 or 1–對應拉高或拉低)
知道了LED對應的端口寄存器的配置需求,接下來,就可以編寫驅動源檔案了
- Step2:myled.c編寫
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/init.h>
#include<linux/delay.h>
#include<asm/uaccess.h>
#include<asm/irq.h>
#include<asm/io.h>
#include<linux/device.h>
MODULE_LICENSE("GPL");
static struct class *my_led_class;
static struct device *my_led_device;
volatile unsigned long *my_led_pbpat=NULL;
volatile unsigned long *my_led_pbdata=NULL;
static int led_open(struct inode *inode,struct file *file);
static int led_read(struct file *filp,char __user *buff,size_t count,loff_t *offp);
static int led_write(struct file *file,const char __user*buf,size_t count,loff_t *ppos);
struct file_operations led_drv_ops={
.open=led_open,
.read=led_read,
.write=led_write,
.owner=THIS_MODULE,
};
int dev_num=0;
static int led_open(struct inode *inode,struct file *file)
{
printk("led open\n");
*my_led_pbpat &=~(0x1<<3);//PBPAT103 0: out,1: input
// *my_led_pbpat |= (0x1<<3);
*my_led_pbdata |=(1<<3);
return 0;
}
static int led_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
int val;
printk("led write\n");
copy_from_user(&val,buf,count);
if(val == '0')
{
//to set 0
*my_led_pbdata &=~(1<<3);
}
else
{
//to set 1
*my_led_pbdata |=(1<<3);
}
return 0;
}
static int led_read(struct file *filp,char __user *buff,size_t cunt,loff_t *offp)
{
printk("led read\n");
return 0;
}
static int led_init(void)
{
dev_num=register_chrdev(0,"led_drv",&led_drv_ops);
if(dev_num<0)
{
printk("sorry,register char device failed!\n");
return -1;
}
my_led_class=class_create(THIS_MODULE,"leddrv");
my_led_device=device_create(my_led_class,NULL,MKDEV(dev_num,0),NULL,"shlled");
printk("init ok\n");
my_led_pbpat=(volatile unsigned long *)ioremap(0x10010130,16);
my_led_pbdata=(volatile unsigned long *)ioremap(0x10010140,16);//0x10010140
return 0;
}
static void led_exit(void)
{
unregister_chrdev(dev_num,"led_drv");
device_unregister(my_led_device);
class_destroy(my_led_class);
iounmap(my_led_pbpat);
iounmap(my_led_pbdata);
printk("led driver exit\n");
}
module_init(led_init);
module_exit(led_exit);
- Step3: Makefile的編寫
obj-m:=myled.o
KERNELDIR :=/home/shl/work/x1500/kernel/
#KERNELDIR :=/usr/src/linux-headers-4.15.0-47-generic/
PWD :=$(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
.PHONEY:clean
clean:
rm -f *.o *.ko *.order *.symvers *.mod*
當然,在編譯makefile需要注意的是,目前編譯需要使用交叉編譯器
因為linux下預設的是gcc,編譯出來的檔案在x1500核心闆上是無法運作的,(我因為這個問題困住了2天),将環境變量中的ARCH和CROSS_COMPILE都修改一下
指令行輸入:
export ARCH=mips
export CROSS_COMPILE=mips-linux-gnu-
執行make ,生成myled.ko檔案
編寫測試檔案來測試目前驅動的功能
- Step4:test.c編寫
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
int main(int argc,char *argv[])
{
char buf[2]={'0','1'};
int cmd;
int fd=open("/dev/shlled",O_RDWR);
if(fd<0)
{
printf("open dev/shlled fail!\n");
return -1;
}
cmd=atoi(argv[1]);
if(cmd==0)
{
write(fd,&buf[0],sizeof(char));
}
else if(cmd==1)
{
write(fd,&buf[1],sizeof(char));
}
else
{
printf("please input 0 or 1");
}
printf("\n");
return 0;
}
編譯生成可執行檔案
mips-linux-gnu-gcc test.c -muclibc -o test
(為何多了一個 -muclibc,這是因為系統的依賴庫和闆子上的依賴庫的版本不一樣,闆子是老闆給的,系統是我自己裝的,老闆又不讓我重新刷系統,我能咋辦)
詳情可以參照動态庫不比對(超連結日後再補)
-
step5: 加載驅動,并測試
(執行代碼摘錄,test,myled.ko目前存放在/tmp目錄下)
/tmp # chmod 777 test
/tmp # insmod myled.ko
[ 736.732691] init ok
/tmp # ls /dev/shlled
/dev/shlled (這裡可以看到,我自己編寫的shlled已經成功顯示在dev下面了)
/tmp # ./test 0
[ 813.653481] led open
[ 813.655778] led write
/tmp # ./test 1
[ 817.871106] led open
[ 817.873552] led write
/tmp # ./test 0
[ 893.370064] led open
[ 893.372463] led write
對應的LED燈亮滅(0 亮,1滅)