本文来源电子发烧友社区,作者:jf_00240724, 帖子地址:https://bbs.elecfans.com/jishu_2284706_1_1.html
今天我们接上一篇继续,需要做两个事情。第一个是重写驱动,第二个是开发霓虹灯应用程序。
第一章 重写驱动
一.1. 知识储备
一.1.1. 卸载驱动
一.1.1.1. linux加载/卸载驱动有两种方法
一.1.1.1.1. modprobe
这里我们主要用这个。
注:在使用这个命令加载模块前先使用depmod -a命令生成modules.dep文件,该文件位于/lib/modules/$(uname -r)目录下;modprobe命令智能地向内核中加载模块或者从内核中移除模块,可载入指定的个别模块,或是载入一组相依的模块。modprobe会根据depmod所产生的依赖关系,决定要载入哪些模块。若在载入过程中出错,modprobe会卸载整组的模块。
载入模块的命令:
(1) 载入指定的模块:modprobe drv.ko
(2) 载入全部模块:modprobe -a
卸载模块的命令:modprobe -r drv.ko
modprobe命令用于智能地向内核中加载模块或者从内核中移除模块。
modprobe可载入指定的个别模块,或是载入一组相依的模块。modprobe会根据depmod所产生的相依关系,决定要载入哪些模块。若在载入过程中发生错误,在modprobe会卸载整组的模块。
选项
-a或--all:载入全部的模块;
-c或--show-conf:显示所有模块的设置信息;
-d或--debug:使用排错模式;
-l或--list:显示可用的模块;
-r或--remove:模块闲置不用时,即自动卸载模块;
-t或--type:指定模块类型;
-v或--verbose:执行时显示详细的信息;
-V或--version:显示版本信息;
-help:显示帮助。
参数 模块名:要加载或移除的模块名称。
实例
查看modules的配置文件:modprobe -c
这里,可以查看modules的配置文件,比如模块的alias别名是什么等。会打印许多行信息,例如其中的一行会类似如下:
alias symbol:ip_conntrack_unregister_notifier ip_conntrack
列出内核中所有已经或者未挂载的所有模块:modprobe -l
这里,我们能查看到我们所需要的模块,然后根据我们的需要来挂载;其实modprobe -l读取的模块列表就位于/lib/modules/`uname -r`目录中;其中uname -r是内核的版本,例如输出结果的其中一行是:
/lib/modules/2.6.18-348.6.1.el5/kernel/net/netfilter/xt_statistic.ko
挂载vfat模块:modprobe vfat
这里,使用格式modprobe 模块名来挂载一个模块。挂载之后,用lsmod可以查看已经挂载的模块。模块名是不能带有后缀的,我们通过modprobe -l所看到的模块,都是带有.ko或.o后缀。
移除已经加载的模块:modprobe -r 模块名
这里,移除已加载的模块,和rmmod功能相同。
一.1.1.1.2. rmmod
这个卸载命令不会把其依赖模块一同卸掉,所以这里我们只作简单介绍。
一.1.2. Linux驱动框架
接下来,我们开始重新写驱动,linux系统下写驱动,其实就是配置相应的硬件寄存器,那本章的霓虹灯驱动,也就是对imX 8的GPIO进行配置,由于裸机实验不同的是,并且驱动要符合linux驱动框架,下面我们来先理解了解一下linux的驱动框架。
一.1.2.0.1. 地址映射
linux的驱动,并不能够直接操作寄存硬件寄存器,而是需要通过MMU即内存管理单元,它的主要功能是
一、以完成虚拟空间的物理空间的映射
二、内存保护设置存储器的访问权限,设置虚拟存储空间的缓冲特性
这里我们会用到两个函数ioremap,iounmap,它们分别是用来获取物理地址空间对应的虚拟地址,和卸载时释放掉之前所做的映射
一.1.2.0.2. linux内存访问函数
对于arm来说,只有IO内存,linux内核建议使用一组操作函数来对映射后的内存进行读写操作
一、读操作函数
<
1 u8 readb(constvolatilevoid __iomem *addr)
2 u16 readw(constvolatilevoid __iomem *addr)
3 u32 readl(constvolatilevoid __iomem *addr)
>
readb、readw和readl这三个函数分别对应8bit、16bit和32bit读操作,参数addr就是要读取写内存地址,返回值就是读取到的数据。
二、写操作函数
<
1void writeb(u8 value,volatilevoid __iomem *addr)
2void writew(u16 value,volatilevoid __iomem *addr)
3void writel(u32 value,volatilevoid __iomem *addr)
>
writeb、writew和writel这三个函数分别对应8bit、16bit和32bit写操作,参数value是要写入的数值,addr是要写入的地址。
接下来我们开始搭建一个linux驱动框架:
<
#include "linux/init.h"
#include "linux/module.h"
#include "linux/fs.h"
#include "linux/types.h"
// struct inode 声明在 linux/fs.h 中
// struct file 声明在 linux/fs.h 中
int led_open (struct inode *i, struct file *f)
{
// printk 声明在 linux/printk.h 中
printk("led open!rn");
return 0;
}
int led_release (struct inode *i, struct file *f)
{
printk("led release!rn");
return 0;
}
// ssize_t 定义在 linux/types.h 中
// __user 定义在 linux/compiler.h 中
// size_t 定义在 linux/types.h 中
// loff_t 定义在 linux/types.h 中
ssize_t led_read (struct file *f, char __user *b, size_t c, loff_t * l)
{
printk("led read!rn");
return 0;
}
ssize_t led_write (struct file *f, const char __user *b, size_t c, loff_t *l)
{
printk("led write!rn");
return 0;
}
// 声明在 linux/fs.h 头文件中
static struct file_operations fops = {
.open = led_open,
.release = led_release,
.read = led_read,
.write = led_write,
};
/* 驱动入口函数 */
static int __init led_init(void)
{
/* 入口函数具体内容 */
int retvalue = 0;
// 声明在 linux/fs.h 头文件中
retvalue = register_chrdev(200,"chrdev",&fops);
if(retvalue < 0){
/* 字符设备注册失败 */
}
return 0;
}
/* 驱动出口函数 */
static void __exit led_exit(void)
{
/* 出口函数具体内容 */
// 声明在 linux/fs.h 头文件中
unregister_chrdev(200,"chrdev");
}
// 声明在 linux/init.h 头文件中
/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(led_init);
module_exit(led_exit);
// 声明在 linux/module.h 头文件中
MODULE_LICENSE("GPL");
>
这只是一个驱动框架没有实际内容。
一.2. 硬件准备
接下来我们要找到电路原理图,和主芯片的操作手册从而找到LED灯对应的哪一个引脚以及其对应的寄存器。
在如下链接中我们找到这样一句话,好吧,原来他并不是GPIO控制的,而是一个iic芯片PCA9533中转控制的,不得不说这个硬件设计让人匪夷所思啊。
Multicolor (RGB) LED (D24)
The phyBOARD-Pollux provides one multicolor (RGB) LED (D24) (see phyBOARD-Pollux Components (Top)). The LEDs are connected to a LED driver (NXP PCA9533/01) controlled by I2C2 bus.
https://www.phytec.de/cdocuments ... wareManual-LEDsLEDs
一.3. 软件-实操
一.3.1. 驱动卸载
基于以上的知识储备,本来想尝试一下卸载得掉leds这个驱动?但最终发现不是我们以上准备的驱动框架,而是sysfs的驱动框架,稍微研究一下,发现有点难度,值得针对性的再写一篇。
在此我们就不去卸载了。
一.3.2. 驱动编写
等研究好了sysfs驱动框架后,再实操一遍iic驱动编写。
第二章 应用程序
二.1. 代码
这里直接上代码:
二.1.1. Leds_app.h
<
typedef enum{
LED1_RED = 1,
LED2_GREEN,
LED3_BULE
}LED_ID;
int leds_control(LED_ID led_id,unsigned char brightness)
>
二.1.2. Leds_app.c
<
int leds_control(LED_ID led_id,unsigned char brightness)
{
int fd = 0;
int ret = 0,len;
/*this buffer size is sufficient to store file path */
char buf[128];
//printf("enter leds_controlrn");
snprintf(buf, sizeof(buf), "/sys/class/leds/user-led%d/brightness",led_id);
fd = open(buf, O_RDWR);
if(fd < 0){
printf("Can't open file %srn", buf);
return -1;
}
len = snprintf(buf, sizeof(buf),"%d", brightness);
ret =write(fd, buf, len);
if (ret < 0)
perror("sysfs_led_write:");
ret = close(fd);
if(ret < 0){
printf("Can't close file for led%drn", led_id);
return -1;
}
// printf("leaveleds_controlrn");
return 0;
}
>
二.1.3. Main.c
<
#include
#include
#include
#include "stdio.h"
#include "leds_app.h"
int main(int argc, char *argv[])
{
int led1_value,led2_value,led3_value;
while (1) {
led1_value = rand()%255;
led2_value = rand()%255;
led3_value = rand()%255;
printf("led1:%3d led2:%3d led3:%3dn",led1_value,led2_value,led3_value);
leds_control(LED3_BULE,0);
leds_control(LED1_RED,led1_value);
sleep(0.9);
//leds_control(LED1_RED,0);
leds_control(LED2_GREEN,led2_value);
sleep(0.8);
leds_control(LED2_GREEN,0);
leds_control(LED3_BULE,led3_value);
sleep(0.5);
}
}
>
二.2. Log
打印出如下log
<
10:48:51: Starting /opt/HelloQuick/bin/HelloQuick ...
Warning: Identity filenot accessible: No such file or directory.
QML debugging is enabled. Only use this in a safe environment.
led1:163 led2:151 led3:162
led1: 85 led2: 83 led3:190
led1:241 led2:252 led3:249
led1:121 led2:107 led3: 82
led1: 20 led2: 19 led3:233
led1:226 led2: 45 led3: 81
led1:142 led2: 31 led3: 86
led1:8 led2: 87 led3: 39
led1:167 led2:5 led3:212
...
>
二.3. 看效果:
请观看底部视频
今天我们接上一篇继续,需要做两个事情。第一个是重写驱动,第二个是开发霓虹灯应用程序。
第一章 重写驱动
一.1. 知识储备
一.1.1. 卸载驱动
一.1.1.1. linux加载/卸载驱动有两种方法
一.1.1.1.1. modprobe
这里我们主要用这个。
注:在使用这个命令加载模块前先使用depmod -a命令生成modules.dep文件,该文件位于/lib/modules/$(uname -r)目录下;modprobe命令智能地向内核中加载模块或者从内核中移除模块,可载入指定的个别模块,或是载入一组相依的模块。modprobe会根据depmod所产生的依赖关系,决定要载入哪些模块。若在载入过程中出错,modprobe会卸载整组的模块。
载入模块的命令:
(1) 载入指定的模块:modprobe drv.ko
(2) 载入全部模块:modprobe -a
卸载模块的命令:modprobe -r drv.ko
modprobe命令用于智能地向内核中加载模块或者从内核中移除模块。
modprobe可载入指定的个别模块,或是载入一组相依的模块。modprobe会根据depmod所产生的相依关系,决定要载入哪些模块。若在载入过程中发生错误,在modprobe会卸载整组的模块。
选项
-a或--all:载入全部的模块;
-c或--show-conf:显示所有模块的设置信息;
-d或--debug:使用排错模式;
-l或--list:显示可用的模块;
-r或--remove:模块闲置不用时,即自动卸载模块;
-t或--type:指定模块类型;
-v或--verbose:执行时显示详细的信息;
-V或--version:显示版本信息;
-help:显示帮助。
参数 模块名:要加载或移除的模块名称。
实例
查看modules的配置文件:modprobe -c
这里,可以查看modules的配置文件,比如模块的alias别名是什么等。会打印许多行信息,例如其中的一行会类似如下:
alias symbol:ip_conntrack_unregister_notifier ip_conntrack
列出内核中所有已经或者未挂载的所有模块:modprobe -l
这里,我们能查看到我们所需要的模块,然后根据我们的需要来挂载;其实modprobe -l读取的模块列表就位于/lib/modules/`uname -r`目录中;其中uname -r是内核的版本,例如输出结果的其中一行是:
/lib/modules/2.6.18-348.6.1.el5/kernel/net/netfilter/xt_statistic.ko
挂载vfat模块:modprobe vfat
这里,使用格式modprobe 模块名来挂载一个模块。挂载之后,用lsmod可以查看已经挂载的模块。模块名是不能带有后缀的,我们通过modprobe -l所看到的模块,都是带有.ko或.o后缀。
移除已经加载的模块:modprobe -r 模块名
这里,移除已加载的模块,和rmmod功能相同。
一.1.1.1.2. rmmod
这个卸载命令不会把其依赖模块一同卸掉,所以这里我们只作简单介绍。
一.1.2. Linux驱动框架
接下来,我们开始重新写驱动,linux系统下写驱动,其实就是配置相应的硬件寄存器,那本章的霓虹灯驱动,也就是对imX 8的GPIO进行配置,由于裸机实验不同的是,并且驱动要符合linux驱动框架,下面我们来先理解了解一下linux的驱动框架。
一.1.2.0.1. 地址映射
linux的驱动,并不能够直接操作寄存硬件寄存器,而是需要通过MMU即内存管理单元,它的主要功能是
一、以完成虚拟空间的物理空间的映射
二、内存保护设置存储器的访问权限,设置虚拟存储空间的缓冲特性
这里我们会用到两个函数ioremap,iounmap,它们分别是用来获取物理地址空间对应的虚拟地址,和卸载时释放掉之前所做的映射
一.1.2.0.2. linux内存访问函数
对于arm来说,只有IO内存,linux内核建议使用一组操作函数来对映射后的内存进行读写操作
一、读操作函数
<
1 u8 readb(constvolatilevoid __iomem *addr)
2 u16 readw(constvolatilevoid __iomem *addr)
3 u32 readl(constvolatilevoid __iomem *addr)
>
readb、readw和readl这三个函数分别对应8bit、16bit和32bit读操作,参数addr就是要读取写内存地址,返回值就是读取到的数据。
二、写操作函数
<
1void writeb(u8 value,volatilevoid __iomem *addr)
2void writew(u16 value,volatilevoid __iomem *addr)
3void writel(u32 value,volatilevoid __iomem *addr)
>
writeb、writew和writel这三个函数分别对应8bit、16bit和32bit写操作,参数value是要写入的数值,addr是要写入的地址。
接下来我们开始搭建一个linux驱动框架:
<
#include "linux/init.h"
#include "linux/module.h"
#include "linux/fs.h"
#include "linux/types.h"
// struct inode 声明在 linux/fs.h 中
// struct file 声明在 linux/fs.h 中
int led_open (struct inode *i, struct file *f)
{
// printk 声明在 linux/printk.h 中
printk("led open!rn");
return 0;
}
int led_release (struct inode *i, struct file *f)
{
printk("led release!rn");
return 0;
}
// ssize_t 定义在 linux/types.h 中
// __user 定义在 linux/compiler.h 中
// size_t 定义在 linux/types.h 中
// loff_t 定义在 linux/types.h 中
ssize_t led_read (struct file *f, char __user *b, size_t c, loff_t * l)
{
printk("led read!rn");
return 0;
}
ssize_t led_write (struct file *f, const char __user *b, size_t c, loff_t *l)
{
printk("led write!rn");
return 0;
}
// 声明在 linux/fs.h 头文件中
static struct file_operations fops = {
.open = led_open,
.release = led_release,
.read = led_read,
.write = led_write,
};
/* 驱动入口函数 */
static int __init led_init(void)
{
/* 入口函数具体内容 */
int retvalue = 0;
// 声明在 linux/fs.h 头文件中
retvalue = register_chrdev(200,"chrdev",&fops);
if(retvalue < 0){
/* 字符设备注册失败 */
}
return 0;
}
/* 驱动出口函数 */
static void __exit led_exit(void)
{
/* 出口函数具体内容 */
// 声明在 linux/fs.h 头文件中
unregister_chrdev(200,"chrdev");
}
// 声明在 linux/init.h 头文件中
/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(led_init);
module_exit(led_exit);
// 声明在 linux/module.h 头文件中
MODULE_LICENSE("GPL");
>
这只是一个驱动框架没有实际内容。
一.2. 硬件准备
接下来我们要找到电路原理图,和主芯片的操作手册从而找到LED灯对应的哪一个引脚以及其对应的寄存器。
在如下链接中我们找到这样一句话,好吧,原来他并不是GPIO控制的,而是一个iic芯片PCA9533中转控制的,不得不说这个硬件设计让人匪夷所思啊。
Multicolor (RGB) LED (D24)
The phyBOARD-Pollux provides one multicolor (RGB) LED (D24) (see phyBOARD-Pollux Components (Top)). The LEDs are connected to a LED driver (NXP PCA9533/01) controlled by I2C2 bus.
https://www.phytec.de/cdocuments ... wareManual-LEDsLEDs
一.3. 软件-实操
一.3.1. 驱动卸载
基于以上的知识储备,本来想尝试一下卸载得掉leds这个驱动?但最终发现不是我们以上准备的驱动框架,而是sysfs的驱动框架,稍微研究一下,发现有点难度,值得针对性的再写一篇。
在此我们就不去卸载了。
一.3.2. 驱动编写
等研究好了sysfs驱动框架后,再实操一遍iic驱动编写。
第二章 应用程序
二.1. 代码
这里直接上代码:
二.1.1. Leds_app.h
<
typedef enum{
LED1_RED = 1,
LED2_GREEN,
LED3_BULE
}LED_ID;
int leds_control(LED_ID led_id,unsigned char brightness)
>
二.1.2. Leds_app.c
<
int leds_control(LED_ID led_id,unsigned char brightness)
{
int fd = 0;
int ret = 0,len;
/*this buffer size is sufficient to store file path */
char buf[128];
//printf("enter leds_controlrn");
snprintf(buf, sizeof(buf), "/sys/class/leds/user-led%d/brightness",led_id);
fd = open(buf, O_RDWR);
if(fd < 0){
printf("Can't open file %srn", buf);
return -1;
}
len = snprintf(buf, sizeof(buf),"%d", brightness);
ret =write(fd, buf, len);
if (ret < 0)
perror("sysfs_led_write:");
ret = close(fd);
if(ret < 0){
printf("Can't close file for led%drn", led_id);
return -1;
}
// printf("leaveleds_controlrn");
return 0;
}
>
二.1.3. Main.c
<
#include
#include
#include
#include "stdio.h"
#include "leds_app.h"
int main(int argc, char *argv[])
{
int led1_value,led2_value,led3_value;
while (1) {
led1_value = rand()%255;
led2_value = rand()%255;
led3_value = rand()%255;
printf("led1:%3d led2:%3d led3:%3dn",led1_value,led2_value,led3_value);
leds_control(LED3_BULE,0);
leds_control(LED1_RED,led1_value);
sleep(0.9);
//leds_control(LED1_RED,0);
leds_control(LED2_GREEN,led2_value);
sleep(0.8);
leds_control(LED2_GREEN,0);
leds_control(LED3_BULE,led3_value);
sleep(0.5);
}
}
>
二.2. Log
打印出如下log
<
10:48:51: Starting /opt/HelloQuick/bin/HelloQuick ...
Warning: Identity filenot accessible: No such file or directory.
QML debugging is enabled. Only use this in a safe environment.
led1:163 led2:151 led3:162
led1: 85 led2: 83 led3:190
led1:241 led2:252 led3:249
led1:121 led2:107 led3: 82
led1: 20 led2: 19 led3:233
led1:226 led2: 45 led3: 81
led1:142 led2: 31 led3: 86
led1:8 led2: 87 led3: 39
led1:167 led2:5 led3:212
...
>
二.3. 看效果:
请观看底部视频
霓虹灯效果
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。
举报投诉
-
led
+关注
关注
240文章
23128浏览量
658230 -
霓虹灯
+关注
关注
0文章
48浏览量
16846 -
开发板
+关注
关注
25文章
4933浏览量
97157 -
PHYTEC
+关注
关注
0文章
19浏览量
3653 -
开发板试用
+关注
关注
3文章
301浏览量
2038
发布评论请先 登录
相关推荐
【PHYTEC开发板试用体验】3 通过开发板拍一张照片
1. 硬件准备接下来,我们的目的是做一个图像识别相关的事情,但由于我们这个Phytec开发板它并没有带一个摄像头外设,所以我们需要给他装
发表于 06-12 14:43
霓虹灯程序员指南
如果您对ARM技术完全陌生,请阅读Cortex-A系列程序员指南,了解有关ARM架构配置文件和一般编程指南的信息。
·霓虹灯技术是ARM高级单指令多数据(SIMD)扩展的实现。
·霓虹灯
发表于 08-17 06:32
评论