精华内容
下载资源
问答
  • STM32单片机WS2812B驱动程序灯效程序

    热门讨论 2017-06-06 17:13:31
    代码主要是基于stm32开发,包括两路ws2812b初始化代码,以及呼吸灯、跑马灯、彩虹灯等灯效程序,均是用pwm+dma的方式发送数据,不占用内存,本程序已用在项目中,可稳定运行。
  • 64位windows平台默认不安装Access的64位ODBC驱动,此下载将安装一系列组件,帮助在现有的 Microsoft ... 此外,还会安装 ODBC 和 OLEDB 驱动程序,供应用程序开发人员在开发与 Office 文件格式连接的应用程序时使用。
  • st-link v2 驱动程序 支持win10

    热门讨论 2018-01-23 22:15:41
    st-link v2的驱动,支持win10。亲测没问题。 STM8调试及烧录所需要的连线: 1.SWIM 2.RESET 3.GND 三根必需 STM32 调试时所需要的线: 1.SWDIO 2.SWCLK 3.GND 三根必需
  • CP5611驱动程序

    热门讨论 2014-08-17 22:06:21
    西门子CP5611通讯卡驱动程序压缩包,需要的请下载
  • ODBC驱动程序ODBC驱动程序

    千次下载 热门讨论 2010-05-19 14:15:16
    在做软件安装包时,把MySQL ODBC 3.51驱动程序必要的文件一起打进安装包,让用户安装软件的时候,自动在系统ODBC 数据源管理器的连接池添加MySQL ODBC 3.51 Driver驱动程序。 InstallSheild添加MySQL ODBC驱动程序
  • win10 64位系统下,HXSP-2108F驱动程序
  • OleDb驱动程序

    热门讨论 2012-09-04 23:54:41
    OleDb驱动程序
  • USB2.0-Serial Win7 64bit驱动程序

    千次下载 热门讨论 2014-12-22 09:04:02
    这是一个串口的转换器的驱动程序 USB转换串口的驱动程序 Win7 64的驱动程序 请谨慎下载然后直接更新驱动程序就好了
  • 深入Linux设备驱动程序内核机制 你懂的 好书 今年新书 驱动
  • MYSQL5.1 ODBC驱动程序64位

    千次下载 热门讨论 2013-04-10 16:53:28
    MYSQL5.1 ODBC驱动程序64位
  • HT1621_stm32_驱动程序

    热门讨论 2014-08-18 15:45:30
    CSDN篡改积分,不知道为什么,改回原来的5积分。 HT1621_stm32_驱动程序,没有背光,没有小数点,只8*7段led ,实现数字显示。
  • Windows设备驱动程序WDF开发及源码

    热门讨论 2012-06-05 19:42:40
    Windows设备驱动程序WDF开发及源码 武安河写的,书比较老,不过属于WDF扫盲教程。 刚学会用WDM的,看这本书最合适不过了。
  • linux 添加字符设备驱动程序及测试程序

    千次下载 热门讨论 2012-02-24 20:47:27
    linux下采用模块方法,添加一个新的设备驱动程序。 要求添加字符设备的驱动。 另附一个应用程序,测试添加的驱动程序
  • XDS100V2 USB仿真器驱动程序

    千次下载 热门讨论 2013-09-06 14:19:38
    XDS100V2 USB仿真器驱动程序。可适用于windows Xp系统,可加载与CCS3.3以上版本。
  • NVIDIA GeForce GT 630M显卡驱动程序 for 32位 在网上找的,很多人需要,特意给网友分享, 直接修改INF的,只能给NVIDIA GeForce GT 630M使用.驱动程序的文件是Nvidia在2012年3月9日发布的. 完整版驱动程序(包括HD ...
  • Sentinel_hasp+hl3.5.zip USB驱动程序

    热门讨论 2013-11-24 15:27:24
    USB驱动程序补丁,用于电脑上面无法识别USB,或出现USB识别有错误的时候,可下载安装即可!
  • 之前显卡驱动出问题了,重新安装一直显示图形驱动程序安装失败 查了网上的方法,基本就是 启动Windows installer服务、策略组、卸干净相关程序、删干净注册表(这个太那什么了,行不通) 全都试过了,还是不行 ...

    之前显卡驱动出问题了,重新安装一直显示图形驱动程序安装失败

    查了网上的方法,基本就是

    启动Windows installer服务、策略组、卸干净相关程序、删干净注册表(这个太那什么了,行不通)

    全都试过了,还是不行

    最后查看系统日志发现

    由于下列错误,ScRegSetValueExW 调用无法运行 Start: 拒绝访问。

    看一下微软社区由于下列错误,ScRegSetValueExW 调用无法运行 Type: 拒绝访问。 (microsoft.com)

    懂了

    关闭什么杀毒软件、什么电脑管家

    再安装,完事

    附显驱下载链接

    NVIDIA 驱动程序下载

    展开全文
  • WIN10平台下Prolific USB-to-Serial Comm Port线的驱动程序及安装方法。
  • 用于解决SQLSERVER连接问题驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接问题JAR包。
  • WINDOWS SERVER 2003网卡驱动程序

    热门讨论 2013-10-25 08:30:08
    我找了2天 好不容易找到的 这个驱动比较少 很多都不能用 所有我找到了赶紧给大家分享下 希望大家也能用(我找的比较多,现在还有4个,我自己都忘了是用的哪个,不过绝对是其中一个,大家自己试试)。 我百度了很多次...
  • PS2键盘驱动程序

    热门讨论 2011-10-08 09:15:16
    PS2键盘驱动程序全部代码都已经实现,学习PS2的参考代码
  • 驱动程序是什么:驱动程序与硬件相关,编写驱动程序要非常了解硬件,同时给应用层提供API函数接口,应用层可以调用这些接口去访问硬件而不必了解硬件. 通俗地解释:比如说你有一个应用是控制每天早上六点钟开灯.应用...
    • 驱动程序是什么:驱动程序与硬件相关,编写驱动程序要非常了解硬件,同时给应用层提供API函数接口,应用层可以调用这些接口去访问硬件而不必了解硬件.

    • 通俗地解释:比如说你有一个应用是控制每天早上六点钟开灯.应用程序只负责在适当的时间做适当的事(到六点了,要开灯了,触发按键信号,这个信号通过驱动程序相应的API接口下发至硬件);具体它不用知道为什么按下键就能开灯,因为按下键后开灯的事情就是驱动程序完成了,驱动程序再去控制硬件管脚发出高低电平信号去驱动灯的开关.

    • 单片机驱动程序文件组成:以led点灯为例,包括三个文件led.h(包括宏定义,变量声明,函数声明),led.c(模块的具体代码实现),main.c(工程的主函数,调用模块函数并适当组合就可以完成工程的项目要求功能)三个文件。

    驱动程序要做的事情:

    1、单片机底层开发一般指利用单片机的固有资源完成的控制功能的软硬件开发,比如时钟、通讯协议(232、485、UART、SPI、IIC、CAN、IR等)、定时器、AD转换、GPIO、外设驱动(各种现场控制、各种环境状况测控、电机控制、电磁阀、继电器控制、键盘扫描、LED和LCD显示)等。

    2、根据时钟树图,观察初始化时钟时需要用到哪些寄存器及各种外设需要的时钟源。

    3、配置定时器的时钟源,分频,比较值和PWM输出。
    (1)当单片机内部有几种不同的时钟源时,必须配置相应的寄存器配置需要的时钟源,如果不进行配置,将会使用单片机上电默认的时钟源。
    (2)时钟源决定着系统的工作频率、定时器定时时间和通信时的波特率。
    (3)定时器分频是为了获得更长的定时时间,如果采用标志位累加的方式来延长定时时间,将会消耗单片机的RAM资源,在低端单片机中,由于RAM资源非常珍贵,因此只能采用定时器分频来延长定时时间。
    (4)比较值对于简单的定时功能就是计算多少个数产生一次中断,对于PWM输出来说就是设置周期和占空比。

    单片机驱动程序与应用程序的区别:应用程序是根据实际应用需求去编写的程序,指的是代码的逻辑部分,直接去调用底层驱动预留的接口,而不需要关心硬件底层的实现方式及实现过程。

    展开全文
  • 此Linux设备驱动程序教程将为您提供有关如何为Linux操作系统编写设备驱动程序的所有必要信息。 本文包含一个易于遵循的实用Linux驱动程序开发示例。 我们将讨论以下内容: 内核日志系统 如何使用角色设备 如何使用...

    翻译来自:
    https://www.apriorit.com/dev-blog/195-simple-driver-for-linux-os
    代码下载

    此Linux设备驱动程序教程将为您提供有关如何为Linux操作系统编写设备驱动程序的所有必要信息。 本文包含一个易于遵循的实用Linux驱动程序开发示例。 我们将讨论以下内容:

    • 内核日志系统
    • 如何使用角色设备
    • 如何使用内核中的用户级内存

    我们将使用Linux内核版本2.6.32。 我们可以使用更新的版本,但是它们的API可能已被修改,因此可能与我们的示例和构建系统中使用的API不同。 学习本教程后,您将熟悉为Linux操作系统编写设备驱动程序或内核模块的过程。

    概述

    Linux有一个单片内核。 因此,为Linux编写设备驱动程序需要与内核进行组合编译。 另一种方法是将驱动程序实现为内核模块,在这种情况下,您无需重新编译内核即可添加其他驱动程序。 我们将关注第二个选项:内核模块。

    在其基础上,模块是专门设计的目标文件。 使用模块时,Linux会将它们加载到其内核空间,从而将它们链接到内核。 Linux内核是使用C编程语言和Assembler开发的。 C实现了内核的主要部分,而Assembler实现了依赖于体系结构的部分。 不幸的是,这些是我们用于编写Linux设备驱动程序的唯一两种语言。 我们不能使用用于Microsoft Windows操作系统内核的C ++,因为Linux内核源代码的某些部分 - 特定的头文件 - 可能包含来自C ++的关键字(例如, delete或new ),而在Assembler中我们可能会遇到诸如’ : : ‘词汇。

    我们在内核上下文中运行模块代码。 这需要开发人员非常专注,因为它需要承担额外的责任:如果开发人员在实现用户级应用程序时出错,在大多数情况下这不会导致用户应用程序之外的问题; 但是如果开发人员在实现内核模块时出错,后果将是系统级别的问题。 幸运的是,Linux内核有一个很好的功能,可以抵御模块代码中的错误。 当内核遇到非严重错误(例如,空指针解除引用)时,您将看到oops消息(Linux操作期间无意义的故障称为oops ),之后将卸载故障模块,允许内核和其他模块像往常一样工作。 此外,您还可以在内核日志中找到准确描述此错误的记录。 但请注意,不建议在oops消息之后继续工作,因为这样做可能会导致不稳定和内核恐慌。

    内核及其模块本质上代表一个程序模块 - 因此请记住,单个程序模块使用单个全局命名空间。 为了最小化它,您必须观察模块导出的内容:导出的全局字符必须唯一命名(常用的解决方法是简单地使用将字符导出为前缀的模块的名称)并且必须剪切到最低限度。

    装载和卸载模块

    要创建一个简单的示例模块,我们不需要做太多工作。 这里有一些代码可以证明这一点:

    #include <linux/init.h>
    #include <linux/module.h>
    
    static int my_init(void)
    {
                           return  0;
    }
    
    static void my_exit(void)
    {
                           return;
    }
    
    module_init(my_init);
    module_exit(my_exit);

    这个模块唯一做的两件事就是加载和卸载自己。 要加载Linux驱动程序,我们调用my_init函数,并卸载它,我们调用my_exit函数。 module_init和module_exit宏通知内核有关驱动程序的加载和卸载。 my_init和my_exit函数必须具有相同的签名,这些签名必须完全如下:

    int init(void);
    void exit(void);

    如果模块需要某个内核版本并且必须包含有关版本的信息,我们需要链接linux / module.h头文件。 尝试加载为另一个内核版本构建的模块将导致Linux操作系统禁止其加载。 出现这种情况的原因是:内核API的更新经常被释放,当您调用其签名已更改的模块函数时,会对整个堆栈造成损害。 module_init和module_exit宏在linux / init.h头文件中声明。

    注册角色设备

    上面的示例模块非常简单; 现在我们将开始处理更复杂的事情。 然而,这个简短的Linux内核驱动程序教程的目的之一是展示如何使用登录内核以及如何与设备文件交互。 这些工具可能很简单,但它们可以为任何驱动程序派上用场,并且在某种程度上,它们使内核模式开发过程更加丰富。

    首先,这里有一些有关设备文件的有用信息。 通常,您可以在/ dev文件夹中找到设备文件。 它们促进了用户和内核代码之间的交互。 如果内核必须接收任何内容,您只需将其写入设备文件即可将其传递给提供此文件的模块; 从设备文件中读取的任何内容都来自提供此文件的模块。 我们可以将设备文件分为两组:字符文件和块文件。 字符文件是非缓冲的,而块文件是缓冲的。 正如其名称所暗示的那样,字符文件允许您逐个字符地读取和写入数据,而块文件允许您只写入整个数据块。 我们将讨论块文件超出本文的范围,并将直接获得字符文件。

    Linux系统有一种通过主设备号识别设备文件的方法, 主设备号识别服务设备文件或一组设备的模块,以及次要设备号 ,用于识别主设备号指定的一组设备中的特定设备。 在驱动程序代码中,我们可以将这些数字定义为常量,也可以动态分配它们。 如果已经使用了定义为常量的数字,系统将返回错误。 当动态分配一个数字时,该函数保留该数字以禁止其他任何数字使用它。

    指定设备的名称

    下面引用的函数用于注册字符设备:

    int register_chrdev (unsigned int   major,
                         const char *   name,
                         const struct   fops);
                         file_operations *

    在这里,我们指定要注册它的设备的名称和主要编号,之后将链接设备和file_operations结构。 如果我们为主参数指定零,该函数将自己分配一个主设备号(即它返回的值)。 如果返回的值为零,则表示成功,而负数表示错误。 两个设备编号均在0-255范围内指定。

    我们将设备名称作为name参数的字符串值传递(如果模块注册单个设备,则此字符串也可以传递模块的名称)。 然后,我们使用此字符串来标识/ sys / devices文件中的设备。 读取,写入和保存等设备文件操作由存储在file_operations结构中的函数指针处理。 这些函数由模块实现,并且指向标识该模块的module结构的指针也存储在file_operations结构中。 在这里你可以看到2.6.32内核版本结构:

    
    struct file_operations {
           struct module *owner;
           loff_t (*llseek) (struct file *, loff_t, int);
           ssize_t (*read) (struct file *, char *, size_t, loff_t *);
           ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
           int (*readdir) (struct file *, void *, filldir_t);
           unsigned int (*poll) (struct file *, struct poll_table_struct *);
           int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
           int (*mmap) (struct file *, struct vm_area_struct *);
           int (*open) (struct inode *, struct file *);
           int (*flush) (struct file *);
           int (*release) (struct inode *, struct file *);
           int (*fsync) (struct file *, struct dentry *, int datasync);
           int (*fasync) (int, struct file *, int);
           int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,
              loff_t *);
        ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
              loff_t *);
        };

    file_operations结构

    如果file_operations结构包含一些不需要的函数,您仍然可以使用该文件而不实现它们。 指向未实现函数的指针可以简单地设置为零。 之后,系统将负责该功能的实现并使其正常运行。 在我们的例子中,我们将实现read函数。

    由于我们要确保只使用我们的Linux驱动程序操作单一类型的设备,因此我们的file_operations结构将是全局和静态的。 相应地,在它创建之后,我们需要静态填充它。 在这里你可以看到这是如何完成的:

    
    static struct file_operations simple_driver_fops = 
    {
        .owner   = THIS_MODULE,
        .read    = device_file_read,
    };

    THIS_MODULE宏的声明包含在linux / module.h头文件中。 我们将宏转换为指向所需模块的模块结构的指针。 稍后,我们将用原型编写函数体,但是现在我们只有指向它的指针,即device_file_read 。

    ssize_t device_file_read (struct file *, char *, size_t, loff_t *);

    file_operations结构允许我们编写几个函数来执行和撤销设备文件的注册。

    static int device_file_major_number = 0;
    static const char device_name[] = "Simple-driver";
    static int register_device(void)
    {
            int result = 0;
            printk( KERN_NOTICE "Simple-driver: register_device() is called." );
            result = register_chrdev( 0, device_name, &simple_driver_fops );
            if( result < 0 )
            {
                printk( KERN_WARNING "Simple-driver:  can\'t register character device with errorcode = %i", result );
                return result;
            }
            device_file_major_number = result;
            printk( KERN_NOTICE "Simple-driver: registered character device with major number = %i and minor numbers 0...255"
                 , device_file_major_number );
            return 0;
    }

    device_file_major _number是一个包含主设备号的全局变量。 当驱动程序的生命周期到期时,此全局变量将撤消设备文件的注册。

    printk Fucntion

    我们已经列出并提到了几乎所有的功能,最后一个是printk功能。 这个函数的声明包含在linux / kernel.h文件中,它的任务很简单:记录内核消息。 毫无疑问,请注意KERN_NOTICE和KERN_WARNING前缀,这些前缀出现在printk的所有列出的格式字符串中。 您可能已经猜到, NOTICE和WARNING表示消息的优先级。 级别从最无关紧要的KERN_DEBUG到关键的KERN_EMERG ,提醒内核不稳定。 这是printk函数和printf库函数之间的唯一区别。

    printk函数形成一个字符串,我们将其写入循环缓冲区, klog守护程序将其读取并将其发送到系统日志。 printk函数的实现允许从内核中的任何地方调用它。 最糟糕的情况是循环缓冲区溢出,这意味着最旧的消息不会记录在日志中。

    下一步是编写一个函数来恢复设备文件的注册。 如果成功注册了设备文件,则device_file_major_number的值将不为零。 这允许我们使用nregister_chrdev function撤销文件的注册,我们在linux / fs.h文件中声明了该nregister_chrdev function 。 主设备号是此函数的第一个参数,后跟包含设备名称的字符串。 register_chrdev和unresister_chrdev函数以类似的方式起作用。

    要注册设备,我们使用以下代码:

    
    void unregister_device(void)
    {
        printk( KERN_NOTICE "Simple-driver: unregister_device() is called" );
        if(device_file_major_number != 0)
        {
            unregister_chrdev(device_file_major_number, device_name);
        }
    }

    使用在用户模式下分配的内存

    我们要编写的函数将从设备中读取字符。 此函数的签名必须适合file_operations结构中的签名:

    ssize_t (*read) (struct file *, char *, size_t, loff_t *);

    让我们看看第一个参数,即指向file结构的指针。 此file结构允许我们获取有关我们正在使用的文件的必要信息,有关此当前文件的私有数据的详细信息,等等。 已读取的数据使用第二个参数(即缓冲区)分配给用户空间。 读取的字节数在第三个参数中定义,我们从第四个参数中定义的某个偏移量开始读取字节。 执行该函数后,必须返回已成功读取的字节数,之后必须刷新偏移量。

    用户在用户模式地址空间中分配特殊缓冲区。 而read函数必须执行的另一个操作是将信息复制到此缓冲区。 来自该空间的指针指向的地址和内核地址空间中的地址可以具有不同的值。 这就是我们不能简单地取消引用指针的原因。 使用这些指针时,我们有一组特定的宏和函数,我们在asm / uaccess.h文件中声明。 在我们的例子中,最合适的函数是copy_to_user() 。 它的名字不言而喻:它只是将特定数据从内核缓冲区复制到用户空间中分配的缓冲区。 此外,它还验证指针是否有效以及缓冲区大小是否足够大。 因此,可以相对容易地处理驱动器中的错误。 这是copy_to_user原型的代码:

    long copy_to_user( void __user *to, const void * from, unsigned long n );

    首先,该函数必须接收三个指针作为参数:指向缓冲区的指针,指向数据源的指针,以及指向复制的字节数的指针。 正如我们所提到的,错误返回的值不是零,并且在成功执行的情况下,该值将为零。 该函数包含_user宏,其任务是执行文档处理。 它有另一个有用的应用程序,它允许我们分析代码是否正确使用地址空间中的指针; 这是使用稀疏分析器完成的,稀疏分析器执行静态代码分析。 确保始终将用户地址空间指针标记为_user 。

    本教程仅包含没有实际设备的Linux驱动程序编程示例。 如果在读取设备文件后不需要返回除文本字符串以外的任何内容,那么这就足够了。

    这是实现read功能的代码:

    static const char    g_s_Hello_World_string[] = "Hello world from kernel mode!\n\0";
    static const ssize_t g_s_Hello_World_size = sizeof(g_s_Hello_World_string);
    static ssize_t device_file_read(
                            struct file *file_ptr
                           , char __user *user_buffer
                           , size_t count
                           , loff_t *position)
    {
        printk( KERN_NOTICE "Simple-driver: Device file is read at offset = %i, read bytes count = %u"
                    , (int)*position
                    , (unsigned int)count );
        /* If position is behind the end of a file we have nothing to read */
        if( *position >= g_s_Hello_World_size )
            return 0;
        /* If a user tries to read more than we have, read only as many bytes as we have */
        if( *position + count > g_s_Hello_World_size )
            count = g_s_Hello_World_size - *position;
        if( copy_to_user(user_buffer, g_s_Hello_World_string + *position, count) != 0 )
            return -EFAULT;    
        /* Move reading position */
        *position += count;
        return count;
    }

    构建内核模块的系统

    在我们为驱动程序编写代码之后,是时候构建它并查看它是否像我们期望的那样工作。 在早期的内核版本(例如2.4)中,构建模块需要来自开发人员的更多动作:编译环境需要单独准备,编译本身需要GCC编译器。 只有在那之后,开发人员才会收到一个* .o文件 - 一个可以加载到内核的模块。 幸运的是,这些时间早已过去,现在这个过程要简单得多。 今天,大部分工作都是由makefile完成的:它启动内核构建系统,并为内核提供有关构建模块所需组件的信息。 从单个源文件构建的模块需要makefile中的单个字符串。 创建此文件后,您只需要启动内核构建系统:

    obj-m := source_file_name.o

    如您所见,这里我们已将源文件名分配给模块,该文件将是* .ko文件。

    相应地,如果有多个源文件,则只需要两个字符串

    obj-m := module_name.o 
    module_name-objs := source_1.o source_2.o … source_n.o

    make命令初始化内核构建系统:

    要构建模块:

    make –C KERNEL_MODULE_BUILD_SYSTEM_FOLDER M=`pwd` modules

    要清理构建文件夹:

    make –C KERNEL_MODULES_BUILD_SYSTEM_FOLDER M=`pwd` clean

    模块构建系统通常位于 /lib/modules/uname -r/build中。 现在是时候准备模块构建系统了。 要构建第一个模块,请从构建系统所在的文件夹中执行以下命令:

    make modules_prepare

    最后,我们将我们学到的所有内容组合到一个makefile中:

    TARGET_MODULE:=simple-module
    # If we are running by kernel building system
    ifneq ($(KERNELRELEASE),)
        $(TARGET_MODULE)-objs := main.o device_file.o
        obj-m := $(TARGET_MODULE).o
    # If we running without kernel build system
    else
        BUILDSYSTEM_DIR:=/lib/modules/$(shell uname -r)/build
        PWD:=$(shell pwd)
    all : 
    # run kernel build system to make module
        $(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) modules
    clean:
    # run kernel build system to cleanup in current directory
        $(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) clean
    load:
        insmod ./$(TARGET_MODULE).ko
    unload:
        rmmod ./$(TARGET_MODULE).ko
    endif

    load目标加载构建模块, unload目标将其从内核中删除。

    在我们的教程中,我们使用了main.c和device_file.c中的代码来编译驱动程序。 生成的驱动程序名为simple-module.ko。

    加载和使用模块

    从源文件文件夹执行以下命令允许我们加载构建的模块:

    sudo make load

    执行此命令后,驱动程序的名称将添加到/proc/modules文件中,而模块注册的设备将添加到/proc/devices文件中。 添加的记录如下所示:

    Character devices: 1 mem 4 tty 4 ttyS … 239 Simple-driver …

    cat /proc/devices | grep  Simple-driver
    $ 239 Simple-driver

    前三个记录包含添加的设备的名称以及与之关联的主设备号。 次编号范围(0-255)允许在/ dev虚拟文件系统中创建设备文件。

    sudo mknod /dev/simple-driver c 239 0

    在我们创建设备文件之后,我们需要执行最终验证以确保我们所做的工作按预期工作。 要验证,我们可以使用cat命令显示内容:

    cat /dev/simple-driver
    $ Hello world from kernel mode!

    参考资料

    1. Linux设备驱动程序, Jonathan Corbet , Alessandro Rubini和Greg Kroah-Hartman的第3版: http://lwn.net/Kernel/LDD3/
    2. Peter Jay Salzman和Ori Pomeranz撰写的Linux内核模块编程指南: http://tldp.org/LDP/lkmpg/2.6/html/lkmpg.html
    3. Linux Cross Reference http://lxr.free-electrons.com/ident

    代码下载

    Demo代码下载

    尾文

    device_file.c有一点编译问题,这样修正。

    copy_to_user -> raw_copy_to_user

    查看内核日志

    dmesg

    玩~

    展开全文
  • Windows.7设备驱动程序开发.pdf

    热门讨论 2014-01-28 22:24:18
    Windows.7设备驱动程序开发.pdf 中文扫描版
  • 舵机驱动程序

    千次阅读 2019-10-25 19:48:03
    舵机驱动程序 SG90 接线图 工作原理 转动方向与占空比有关 占空比大往右转 占空比小往左转 占空比0.5停止 程序 链接:https://pan.baidu.com/s/1F7BgWfz7RpCck3c_–Sirg 提取码:6d3a void TIM14_PWM_Init(u32 ...

    舵机驱动程序

    SG90

    在这里插入图片描述

    接线图

    在这里插入图片描述

    工作原理

    在这里插入图片描述
    转动方向与占空比有关
    占空比大往右转
    占空比小往左转
    占空比0.5停止

    程序

    链接:https://pan.baidu.com/s/1F7BgWfz7RpCck3c_–Sirg
    提取码:6d3a

    信号线 F9

    void TIM14_PWM_Init(u32 arr,u32 psc)
    {		 					 
    	//此部分需手动修改IO口设置
    	
    	GPIO_InitTypeDef GPIO_InitStructure;
    	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    	TIM_OCInitTypeDef  TIM_OCInitStructure;
    	
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE);  	//TIM14时钟使能    
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); 	//使能PORTF时钟	
    	
    	GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //GPIOF9复用为定时器14
    	
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;           //GPIOF9
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //复用功能
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度100MHz
    	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽复用输出
    	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉
    	GPIO_Init(GPIOF,&GPIO_InitStructure);              //初始化PF9
    	  
    	TIM_TimeBaseStructure.TIM_Prescaler=psc;  //定时器分频
    	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
    	TIM_TimeBaseStructure.TIM_Period=arr;   //自动重装载值
    	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
    	
    	TIM_TimeBaseInit(TIM14,&TIM_TimeBaseStructure);//初始化定时器14
    	
    	//初始化TIM14 Channel1 PWM模式	 
    	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
     	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性低
    	TIM_OC1Init(TIM14, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM1 4OC1
    
    	TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);  //使能TIM14在CCR1上的预装载寄存器
     
      TIM_ARRPreloadConfig(TIM14,ENABLE);//ARPE使能 
    	
    	TIM_Cmd(TIM14, ENABLE);  //使能TIM14
     
    										  
    }  
    
    
    ```c
     	TIM14_PWM_Init(200-1,8400-1);	
    

    arr=200 -1 psc=8400-1

    在代码中要特别注意的是时基结构体的TIM_Period(自动重装载寄存器值,简称arr)和TIM_Prescaler(预分频寄存器值,简称psc),因为这两个决定了输出PWM信号的周期。具体的周期计算公式为:周期=(arr+1)*(psc+1)/CLK。其中CLK为计数器的时钟频率,我的是84MHZ,也就是84000000。最后计算结果单位为秒,结果为0.02s,也就是20ms。这样的配置就是为了让输出的PWM信号达到前面说到的舵机要求的20ms周期。

    这段话引用CSDN博主「_pray」的原创文章
    原文链接:https://blog.csdn.net/qq_40499719/article/details/81267895

    核心

    key=KEY_Scan(0);		//得到键值
    	   	if(key)
    		{						   
    			switch(key)
    			{				 
    				case WKUP_PRES:	//控制蜂鸣器
    					TIM_SetCompare1(TIM14,5);
    				delay_ms(40);**这个时间可以控制转过的角度**
    				//TIM_SetCompare1(TIM14,15);
    					break;
    				case KEY0_PRES:	//控制LED0翻转
    					TIM_SetCompare1(TIM14,10);
    				            delay_ms(40);
    					break;
    				case KEY1_PRES:	//控制LED1翻转	 
    					TIM_SetCompare1(TIM14,20);
    				delay_ms(20);
    					break;
    				case KEY2_PRES:	//同时控制LED0,LED1翻转 
    					TIM_SetCompare1(TIM14,25);
    				delay_ms(20);
    //					LED1=!LED1;
    					break;
    			}
    
    			case WKUP_PRES:	//控制蜂鸣器
    				TIM_SetCompare1(TIM14,5);
    			delay_ms(40);
    

    ** delay_ms(40)这个时间可以控制转过的角度**

    例如当 TIM_SetCompare1(TIM14,10);时转动角度为45°
    因为周期是20ms ,所以当delay_ms(40)时相当于执行两个周期
    也就是转过了两个45°

    时间长度可由自己控制
    达到了按照一定角度转动之后能够停下来的目的

    如果有写的不对的地方欢迎大家批评指正,共同进步!!!

    展开全文
  • 印度黑客大牛Linux内核重要开发者的力作《精通Linux设备驱动程序开发》,中文版由宋宝华等国内高手翻译,是学习Linux内核驱动的重要参考用书!本资源是中英文版的合集,物超所值哦!楼主收集了目前已知的所有Linux...
  • Mysql(5.5)jdbc驱动程序

    热门讨论 2012-04-25 18:49:47
    mysql-connector-java-5.1.19,jdbc驱动程序(驱动包)
  • postgreSQL的ODBC驱动程序(X64)

    热门讨论 2013-01-05 06:25:14
    postgreSQL的ODBC驱动程序(X64)
  • Linux设备驱动程序(LDD)第三版英文PDF(附书中源码)

    千次下载 热门讨论 2013-04-11 23:40:47
    国外经典Linux驱动书籍《Linux设备驱动程序(LDD)第三版英文PDF》,高清PDF并附有书中源码!

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,064,804
精华内容 425,921
关键字:

驱动程序