• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

嵌入式LinuxV3s led驱动开发

武飞扬头像
liefyuan
帮助5

原理图

学新通

方式一:sysfs的GPIO方式控制LED灯

PG1是SDC1的SDC1_CMD口
直接操作sysfs的GPIO控制失败了,应该是设备树里面有声明了!

# echo 193 > /sys/class/gpio/export
[96509.628703] sun8i-v3s-pinctrl 1c20800.pinctrl: pin PG1 already requested by 1c10000.mmc; cannot claim for 1c20800.pinctrl:193

[96509.641625] sun8i-v3s-pinctrl 1c20800.pinctrl: pin-193 (1c20800.pinctrl:193) status -22
sh: write error: Invalid argument

所以需要注释掉:

/*
&mmc1 {
	broken-cd;
	bus-width = <4>;
	vmmc-supply = <&reg_vcc3v3>;
	status = "okay";
};
*/

使用arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dts文件中的原有配置,就不用改了:
学新通
但是还需要打开:arch/arm/boot/dts/sun8i-v3s-licheepi-zero-dock.dts

leds {
                status = "okay";
        };

再运行一下就可以了

# echo 193 > /sys/class/gpio/export
[  152.427408] sun8i-v3s-pinctrl 1c20800.pinctrl: 1c20800.pinctrl supply vcc-pg not found, using dummy regulator
# ls /sys/class/gpio
export     gpio193    gpiochip0  unexport
# echo out > /sys/class/gpio/gpio193/direction # 设置引脚为输出模式
# echo 0 > /sys/class/gpio/gpio193/value  # 打开LED灯
# echo 1 > /sys/class/gpio/gpio193/value # 关闭LED灯

可以,控制灯光没有问题。

方式二:设备驱动的方式

编写led驱动模块

led_dev.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/io.h>       //含有 ioremap 函数 iounmap 函数
#include <linux/uaccess.h>  //含有 copy_from_user 函数和含有 copy_to_user 函数
#include <linux/device.h> //含有类相关的设备函数
#include <linux/cdev.h>

#define GPIOG_CFG0 (0x01C208D8)
#define GPIOG_CFG1 (0x01C208DC)
#define GPIOG_DATA (0x01C208E8)
#define GPIOG_PUL0 (0x01C208F4)

static dev_t led_dev_num;       //定义一个设备号
static struct cdev *led_dev;    //定义一个设备管理结构体指针
static struct class *led_class; //定义一个设备类
static struct device *led0;     //定义一个设备

size_t *gpiog_cfg0; //存储虚拟地址到物理地址映射
size_t *gpiog_cfg1; //存储虚拟地址到物理地址映射
size_t *gpiog_data; //存储虚拟地址到物理地址映射
size_t *gpiog_pul0; //存储虚拟地址到物理地址映射

static int led_open(struct inode *inode, struct file *file)
{
	/* GPIOG 配置 */
	*((volatile size_t*)gpiog_cfg1) &= ~(7<<16); //清除配置寄存器
	*((volatile size_t*)gpiog_cfg1) |= (1<<1);  //配置 GPIOG1 为输出模式
	*((volatile size_t*)gpiog_pul0) &= ~(3<<16); //清除上/下拉寄存器
	*((volatile size_t*)gpiog_pul0) |= (1<<1);  //配置 GPIOG1 为上拉模式

	printk(KERN_DEBUG"open led!!!\n");
	return 0;
}

static int led_close(struct inode *inode, struct file *filp)
{
	printk(KERN_DEBUG"close led!!!\n");
	return 0;
}

static int led_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
	int ret;
	size_t status = ((*((volatile size_t*)gpiog_data))>>1)&0x01;//获取 GPIOG1 状态
	ret = copy_to_user(buff,&status,4); //将内核空间拷贝到用户空间 buff
	if(ret < 0)
		printk(KERN_DEBUG"read error!!!\n"); //输出信息linux 驱动开发指南 | 李山文 著
	else
		printk(KERN_DEBUG"read led ok!!!\n"); //输出信息
	return 0;
}

static int led_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
{
	int ret;
	size_t status;
	ret = copy_from_user(&status,buff,4); //将用户空间拷贝到内核空间的 status
	if(ret < 0)
		printk(KERN_DEBUG"write error!!!\n"); //输出信息
	else
		printk(KERN_DEBUG"write led ok!!!\n"); //输出信息
		*((volatile size_t*)gpiog_data) &= ~(1<<1) ;//清除 GPIOG1 状态
	if(status)
		*((volatile size_t*)gpiog_data) |= (1<<1);//设置 GPIOG1 状态 1
	return 0;
}

static struct file_operations led_ops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = led_close,
};

static int __init led_init(void)
{
	int ret;
	led_dev = cdev_alloc(); //动态申请一个设备结构体
	if(led_dev == NULL)
	{
		printk(KERN_WARNING"cdev_alloc failed!\n");
		return -1;
	}
	ret = alloc_chrdev_region(&led_dev_num,0,1,"led"); //动态申请一个设备号
	if(ret !=0)
	{
		printk(KERN_WARNING"alloc_chrdev_region failed!\n");
		return -1;
	}
	led_dev->owner = THIS_MODULE; //初始化设备管理结构体的 owner 为 THIS_MODULE
	led_dev->ops = &led_ops; //初始化设备操作函数指针为 led_ops 函数
	cdev_add(led_dev,led_dev_num,1); //将设备添加到内核中
	led_class = class_create(THIS_MODULE, "led_class"); //创建一个名为 led_class 的类linux 驱动开发指南 | 李山文 著

	if(led_class == NULL)
	{
		printk(KERN_WARNING"led_class failed!\n");
		return -1;
	}
	led0 = device_create(led_class,NULL,led_dev_num,NULL,"led0");//创建一个设备名为 led0
	if(IS_ERR(led0))
	{
		printk(KERN_WARNING"device_create failed!\n");
		return -1;
	}
	gpiog_cfg0 = ioremap(GPIOG_CFG0,4); //将 GPIOG_CFG0 物理地址映射为虚拟地址
	gpiog_cfg1 = ioremap(GPIOG_CFG1,4); //将 GPIOG_CFG1 物理地址映射为虚拟地址
	gpiog_data = ioremap(GPIOG_DATA,4); //将 GPIOG_DATA 物理地址映射为虚拟地址
	gpiog_pul0 = ioremap(GPIOG_PUL0,4); //将 GPIOG_PUL0 物理地址映射为虚拟地址
	return 0;
}

static void __exit led_exit(void)
{
	cdev_del(led_dev); //从内核中删除设备管理结构体
	unregister_chrdev_region(led_dev_num,1); //注销设备号
	device_destroy(led_class,led_dev_num); //删除设备节点
	class_destroy(led_class); //删除设备类
	iounmap(gpiog_cfg0); //取消 GPIOG_CFG0 映射
	iounmap(gpiog_cfg1); //取消 GPIOG_CFG1 映射
	iounmap(gpiog_data); //取消 GPIOG_DATA 映射
	iounmap(gpiog_pul0); //取消 GPIOG_PUL0 映射
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL"); //不加的话加载会有错误提醒
MODULE_AUTHOR("liefyuan@qq.com"); //作者
MODULE_VERSION("0.1"); //版本
MODULE_DESCRIPTION("led_dev"); //简单的描述


学新通

Makefile

KERN_DIR = /home/liefyuan/Liefyuan/bingpi-v3s/linux-zero-5.2.y
all:
	make -C $(KERN_DIR) M=$(shell pwd) modules
clean:
	rm -rf *.order *o *.symvers *.mod.c *.mod *.ko
obj-m  = led_dev.o

编译成驱动模块

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
liefyuan@ubuntu:~/Liefyuan/bingpi-v3s/drivers/led$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
make -C /home/liefyuan/Liefyuan/bingpi-v3s/linux-zero-5.2.y M=/home/liefyuan/Liefyuan/bingpi-v3s/drivers/led modules
make[1]: Entering directory '/home/liefyuan/Liefyuan/bingpi-v3s/linux-zero-5.2.y'
  CC [M]  /home/liefyuan/Liefyuan/bingpi-v3s/drivers/led/led_dev.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/liefyuan/Liefyuan/bingpi-v3s/drivers/led/led_dev.mod.o
  LD [M]  /home/liefyuan/Liefyuan/bingpi-v3s/drivers/led/led_dev.ko
make[1]: Leaving directory '/home/liefyuan/Liefyuan/bingpi-v3s/linux-zero-5.2.y'

scp命令拷贝模块到开发板上去

scp led_dev.ko root@192.168.1.103:/home

加载模块

insmod led_dev.ko

如下出现了led0节点。

# insmod led_dev.ko
[ 4514.268184] led_dev: loading out-of-tree module taints kernel.
# ls /dev
bus                 ptyvb               ttyda
console             ptyvc               ttydb
cpu_dma_latency     ptyvd               ttydc
fb0                 ptyve               ttydd
fd                  ptyvf               ttyde
full                ptyw0               ttydf
gpiochip0           ptyw1               ttye0
i2c-0               ptyw2               ttye1
input               ptyw3               ttye2
kmsg                ptyw4               ttye3
led0                ptyw5               ttye4
log                 ptyw6               ttye5
mem                 ptyw7               ttye6
memory_bandwidth    ptyw8               ttye7
mmcblk0             ptyw9               ttye8
mmcblk0p1           ptywa               ttye9
mmcblk0p2           ptywb               ttyea
network_latency     ptywc               ttyeb
network_throughput  ptywd               ttyec
null                ptywe               ttyed
ptmx                ptywf               ttyee
pts     
学新通

编写led测试应用

led_app.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char **argv)
{
    int fd;
    char* filename=NULL;
    int val;
    filename = argv[1];
    fd = open(filename, O_RDWR);//打开 dev/设备文件
    if (fd < 0)//小于 0 说明没有成功
    {
        printf("error, can't open %s\n", filename);
        return 0;
    }
    if(argc !=3)
    {
        printf("usage: ./led_app.elf [device] [on/off]\n"); //打印用法
    }
    if(!strcmp(argv[2], "on")) //如果输入等于 on,则 LED 亮
        val = 0;
    else if(!strcmp(argv[2], "off")) //如果输入等于 off,则 LED 灭
        val = 1;
    else
        goto error;

    write(fd, &val, 4);//操作 LED
    close(fd);
    return 0;
    error:
    printf("usage: ./led_app.elf [device] [on/off]\n"); //打印用法
    close(fd);
    return -1;
}
学新通

编译应用

arm-linux-gnueabihf-gcc led_app.c -o led_app.elf

拷贝应用到开发板进行测试

liefyuan@ubuntu:~/Liefyuan/bingpi-v3s/drivers/led$ scp led_app.elf root@192.168.1.103:/home
root@192.168.1.103's password: 
led_app.elf                                                                                   100%   10KB  10.4KB/s   00:00    
  • 开灯:./led_app.elf /dev/led0 on
  • 关灯:./led_app.elf /dev/led0 off

完结!

问题记录:

编译ko出错:error: implicit declaration of function ‘copy_from_user’ [-Werror=implicit-function-declaration]

liefyuan@ubuntu:~/Liefyuan/bingpi-v3s/drivers/led$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
make -C /home/liefyuan/Liefyuan/bingpi-v3s/linux-zero-5.2.y M=/home/liefyuan/Liefyuan/bingpi-v3s/drivers/led modules
make[1]: Entering directory '/home/liefyuan/Liefyuan/bingpi-v3s/linux-zero-5.2.y'
  CC [M]  /home/liefyuan/Liefyuan/bingpi-v3s/drivers/led/led_dev.o
/home/liefyuan/Liefyuan/bingpi-v3s/drivers/led/led_dev.c: In function ‘led_read’:
/home/liefyuan/Liefyuan/bingpi-v3s/drivers/led/led_dev.c:52:2: error: implicit declaration of function ‘copy_to_user’ [-Werror=implicit-function-declaration]
  ret = copy_to_user(buff,&status,4); //将内核空间拷贝到用户空间 buff
  ^
/home/liefyuan/Liefyuan/bingpi-v3s/drivers/led/led_dev.c: In function ‘led_write’:
/home/liefyuan/Liefyuan/bingpi-v3s/drivers/led/led_dev.c:64:2: error: implicit declaration of function ‘copy_from_user’ [-Werror=implicit-function-declaration]
  ret = copy_from_user(&status,buff,4); //将用户空间拷贝到内核空间的 status
  ^
cc1: some warnings being treated as errors
scripts/Makefile.build:284: recipe for target '/home/liefyuan/Liefyuan/bingpi-v3s/drivers/led/led_dev.o' failed
make[2]: *** [/home/liefyuan/Liefyuan/bingpi-v3s/drivers/led/led_dev.o] Error 1
Makefile:1612: recipe for target '_module_/home/liefyuan/Liefyuan/bingpi-v3s/drivers/led' failed
make[1]: *** [_module_/home/liefyuan/Liefyuan/bingpi-v3s/drivers/led] Error 2
make[1]: Leaving directory '/home/liefyuan/Liefyuan/bingpi-v3s/linux-zero-5.2.y'
Makefile:3: recipe for target 'all' failed
make: *** [all] Error 2
学新通

解决办法:
将头文件#include <asm/uaccess.h>改为#include <linux/uaccess.h>

编译ko文件的方法需要指定架构和交叉编译

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

scp命令拷贝出现问题

liefyuan@ubuntu:~/Liefyuan/bingpi-v3s/drivers/led$ scp led_dev.ko root@192.168.1.103:/home
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
SHA256:Ecz1YO43FOYFccOT J5WVNVVsyAv6QjrdPacppIWLFE.
Please contact your system administrator.
Add correct host key in /home/liefyuan/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /home/liefyuan/.ssh/known_hosts:3
  remove with:
  ssh-keygen -f "/home/liefyuan/.ssh/known_hosts" -R 192.168.1.103
ECDSA host key for 192.168.1.103 has changed and you have requested strict checking.
Host key verification failed.
lost connection
学新通

编辑文件vi /home/liefyuan/.ssh选择know_hosts选项回车,进入另一个文件界面,然后把最后一行秘钥删除,然后保存,退出。就可以了。

liefyuan@ubuntu:~/Liefyuan/bingpi-v3s/drivers/led$ scp led_dev.ko root@192.168.1.103:/home
The authenticity of host '192.168.1.103 (192.168.1.103)' can't be established.
ECDSA key fingerprint is SHA256:Ecz1YO43FOYFccOT J5WVNVVsyAv6QjrdPacppIWLFE.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.103' (ECDSA) to the list of known hosts.
root@192.168.1.103's password: 
led_dev.ko                                                                                    100% 7068     6.9KB/s   00:00    

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhehcj
系列文章
更多 icon
同类精品
更多 icon
继续加载