目录

一、 硬件原理图

二、 驱动程序

三、 应用程序

四、 Makefile

五、操作

一、 硬件原理图

        又是非常经典的点灯环节 ,每次学新语言第一步都是hello world,拿到新板子或者学习新的操作系统,第一步就是点灯。

        LED 的驱动方式,常见的有四种。 ① 使用引脚输出 3.3V 点亮 LED,输出 0V 熄灭 LED。 ② 使用引脚拉低到 0V 点亮 LED,输出 3.3V 熄灭 LED。 有的芯片为了省电等原因,其引脚驱动能力不足,这时可以使用三极管驱动。 ③ 使用引脚输出 1.2V 点亮 LED,输出 0V 熄灭 LED。 ④ 使用引脚输出 0V 点亮 LED,输出 1.2V 熄灭 LED。 由此,主芯片引脚输出高电平/低电平,即可改变 LED 状态,而无需关注 GPIO 引脚输出的是 3.3V 还是 1.2V。所以简称输出 1 或 0: ⚫ 逻辑 1-->高电平 ⚫ 逻辑 0-->低电平         SOC级别的芯片通常电压都比较低,像我们之前用的exynos4412他是1.8V的,我们的i.MX6ULL则是可以做到更低的逻辑1,1.2V。现在最新的技术好像是0.8V的。电压降低的好处就是我们的功耗大幅减小。MCU为什么不降低呢,因为它是控制器需要高电压的驱动环境,所以一般都是3.3V和5V的。

这是板子上的LED的原理图

6ull的GPIO是这样描述的

看上面的原理图我找到了

        第五组GPIO的第三个也就是4*32+4-1 = 131

        每组GPIO有32个,我们0开始所以就是128+3 ,131就是我们的GPIO号

        知道这个就差不多可以写驱动程序了,这就是由操作系统和无操作系统的区别,裸机开发的话我们还要找到其它的寄存器,上面说到的那八个都要找到,但是因为GPIO是通用外设,操作系统已经处理过了,所以我们用的话就会很轻松,甚至可以直接给dev下的GPIO设备写值来控制。

然后我们就可以写代码了

二、 驱动程序

#include "asm-generic/errno-base.h"

#include "asm-generic/gpio.h"

#include "asm/uaccess.h"

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

struct gpio_desc{

int gpio;

int irq;

char *name;

int key;

struct timer_list key_timer;

} ;

static struct gpio_desc gpios[2] = {

{131, 0, "led0", },

//{132, 0, "led1", },

};

/* 主设备号 */

static int major = 0;

static struct class *gpio_class;

/* 实现对应的open/read/write等函数,填入file_operations结构体 */

static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)

{

char tmp_buf[2];

int err;

int count = sizeof(gpios)/sizeof(gpios[0]);

if (size != 2)

return -EINVAL;

err = copy_from_user(tmp_buf, buf, 1);

if (tmp_buf[0] >= count)

return -EINVAL;

tmp_buf[1] = gpio_get_value(gpios[tmp_buf[0]].gpio);

err = copy_to_user(buf, tmp_buf, 2);

return 2;

}

static ssize_t gpio_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)

{

unsigned char ker_buf[2];

int err;

if (size != 2)

return -EINVAL;

err = copy_from_user(ker_buf, buf, size);

if (ker_buf[0] >= sizeof(gpios)/sizeof(gpios[0]))

return -EINVAL;

gpio_set_value(gpios[ker_buf[0]].gpio, ker_buf[1]);

return 2;

}

/* 定义自己的file_operations结构体 */

static struct file_operations gpio_key_drv = {

.owner = THIS_MODULE,

.read = gpio_drv_read,

.write = gpio_drv_write,

};

/* 在入口函数 */

static int __init gpio_drv_init(void)

{

int err;

int i;

int count = sizeof(gpios)/sizeof(gpios[0]);

printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

for (i = 0; i < count; i++)

{

/* set pin as output */

err = gpio_request(gpios[i].gpio, gpios[i].name);

if (err < 0) {

printk("can not request gpio %s %d\n", gpios[i].name, gpios[i].gpio);

return -ENODEV;

}

gpio_direction_output(gpios[i].gpio, 1);

}

/* 注册file_operations */

major = register_chrdev(0, "100ask_led", &gpio_key_drv); /* /dev/gpio_desc */

gpio_class = class_create(THIS_MODULE, "100ask_led_class");

if (IS_ERR(gpio_class)) {

printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

unregister_chrdev(major, "100ask_led_class");

return PTR_ERR(gpio_class);

}

device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "100ask_led"); /* /dev/100ask_gpio */

return err;

}

/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数

*/

static void __exit gpio_drv_exit(void)

{

int i;

int count = sizeof(gpios)/sizeof(gpios[0]);

printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

device_destroy(gpio_class, MKDEV(major, 0));

class_destroy(gpio_class);

unregister_chrdev(major, "100ask_led");

for (i = 0; i < count; i++)

{

gpio_free(gpios[i].gpio);

}

}

/* 7. 其他完善:提供设备信息,自动创建设备节点 */

module_init(gpio_drv_init);

module_exit(gpio_drv_exit);

MODULE_LICENSE("GPL");

三、 应用程序

#include

#include

#include

#include

#include

#include

#include

#include

#include

static int fd;

//int led_on(int which);

//int led_off(int which);

//int led_status(int which);

/*

* ./led_test <0|1|2|..> on

* ./led_test <0|1|2|..> off

* ./led_test <0|1|2|..>

*/

int main(int argc, char **argv)

{

int ret;

char buf[2];

int i;

/* 1. 判断参数 */

if (argc < 2)

{

printf("Usage: %s <0|1|2|...> [on | off]\n", argv[0]);

return -1;

}

/* 2. 打开文件 */

fd = open("/dev/100ask_led", O_RDWR);

if (fd == -1)

{

printf("can not open file /dev/100ask_led\n");

return -1;

}

if (argc == 3)

{

/* write */

buf[0] = strtol(argv[1], NULL, 0);

if (strcmp(argv[2], "on") == 0)

buf[1] = 0;

else

buf[1] = 1;

ret = write(fd, buf, 2);

}

else

{

buf[0] = strtol(argv[1], NULL, 0);

ret = read(fd, buf, 2);

if (ret == 2)

{

printf("led %d status is %s\n", buf[0], buf[1] == 0 ? "on" : "off");

}

}

close(fd);

return 0;

}

四、 Makefile

韦东山老师的makefile写的有点太潦草了,我们来优化一下

CC := $(CROSS_COMPILE)gcc

FILE_NAME = led_test

DRIVER_NAME = led_drv

# 定义NFS根文件系统目录

FS_FILE = ~/nfs_rootfs

KERN_DIR = /home/book/program/100ask_imx6ull_mini-sdk/Linux-4.9.88 # 板子所用内核源码的目录

# all:

# make -C $(KERN_DIR) M=`pwd` modules

# $(CROSS_COMPILE)gcc -o led_test led_test.c

# 默认目标

all:

@echo "Starting build process..."

@echo "Building kernel modules..."

make -C $(KERN_DIR) M=$(PWD) modules

@echo "Building $(FILE_NAME) test program..."

$(CC) -o $(FILE_NAME) $(FILE_NAME).c

# 安装目标

install:

@echo "Installing $(DRIVER_NAME).ko to $(FS_FILE)..."

cp ./$(DRIVER_NAME).ko $(FS_FILE)

@echo "$(DRIVER_NAME).ko installed."

@echo "Installing $(FILE_NAME) to $(FS_FILE)..."

cp ./$(FILE_NAME) $(FS_FILE)

@echo "$(FILE_NAME) installed."

clean:

make -C $(KERN_DIR) M=`pwd` modules clean

rm -rf modules.order led_test

# 参考内核源码drivers/char/ipmi/Makefile

# 要想把a.c, b.c编译成ab.ko, 可以这样指定:

# ab-y := a.o b.o

# obj-m += ab.o

obj-m += led_drv.o

# 声明伪目标

.PHONY: all clean install

五、操作

每次都要重新配置网络很难受所以我这面写了个脚本上机自动配置ip并且挂载nfs 

#!/bin/bash

# 定义变量

NFS_SERVER="192.168.5.10"

NFS_SHARE="/home/book/nfs_rootfs"

IPADDR="192.168.5.110"

MOUNT_POINT="/mnt"

INTERFACE="eth0"

# 设置本机IP

sleep 1

ifconfig $INTERFACE $IPADDR

sleep 1

# 测试与NFS服务器的连通性

ping -c 1 $NFS_SERVER > /dev/null 2>&1

if [ $? -eq 0 ]; then

echo "NFS服务器 $NFS_SERVER 连通性正常"

else

echo "无法与NFS服务器 $NFS_SERVER 建立连接"

exit 1

fi

# 检查挂载点是否存在,如果不存在则创建

if [ ! -d "$MOUNT_POINT" ]; then

mkdir -p "$MOUNT_POINT"

fi

# 尝试挂载NFS共享

mount -t nfs -o nolock,vers=3 $NFS_SERVER:$NFS_SHARE $MOUNT_POINT

if [ $? -eq 0 ]; then

echo "NFS共享已成功挂载到 $MOUNT_POINT"

else

echo "无法挂载NFS共享到 $MOUNT_POINT"

exit 1

fi

最后我们上传一下

相关文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: