• 驱动开发--创建设备文件--控制LED灯


    1、手动创建设备文件

    cat  /proc/devices 查看主设备号

    sudo  mknod hello(路径:任意的)   c/b(C代表字符设备 b代表块设备)主设备号  次设备号

    生成hello:应用层可以打开的文件

    设置驱动层程序:hello.c

    1. #include
    2. #include
    3. #include
    4. #include //添加头文件
    5. #define CNAME "hello"
    6. int major=0;
    7. ssize_t mycdev_read (struct file *file, char __user *user, size_t size, loff_t * loff)
    8. {
    9. printk("this is read\n");
    10. return 0;
    11. }
    12. ssize_t mycdev_write (struct file *file, const char __user *user, size_t size, loff_t *loff)
    13. {
    14. printk("this is write\n");
    15. return 0;
    16. }
    17. int mycdev_open (struct inode *inode, struct file *file)
    18. {
    19. printk("this is open\n");
    20. return 0;
    21. }
    22. int mycdev_release (struct inode *inode, struct file *file)
    23. {
    24. printk("this is close\n");
    25. return 0;
    26. }
    27. const struct file_operations fops={
    28. .open=mycdev_open,
    29. .read=mycdev_read,
    30. .write=mycdev_write,
    31. .release=mycdev_release,
    32. };
    33. static int __init hello_init(void)//入口
    34. {
    35. major=register_chrdev(major,CNAME,&fops);
    36. if(major<0)
    37. {
    38. printk("register chrdev error");
    39. }
    40. return 0;
    41. }
    42. static void __exit hello_exit(void)//出口
    43. {
    44. unregister_chrdev(major,CNAME);
    45. }
    46. module_init(hello_init);//告诉内核驱动的入口
    47. module_exit(hello_exit);//告诉内核驱动的出口
    48. MODULE_LICENSE("GPL");

     设置应用层程序:test.c

    2、应用程序如何将数据传递给驱动

    (读写的方向是站在用户的角度来说的)

    #include

    int copy_from_user(void *to, const void __user *from, int n)

    功能:从用户空间拷贝数据到内核空间

    参数:

    @to  :内核中内存的首地址

    @from:用户空间的首地址

    @n   :拷贝数据的长度(字节)

    返回值:成功返回0,失败返回未拷贝的字节的个数

    int copy_to_user(void __user *to, const void *from, int n)

    功能:从内核空间拷贝数据到用户空间

    参数:

    @to  :用户空间内存的首地址

    @from:内核空间的首地址  __user需要加作用是告诉编译器这是用户空间地址

    @n   :拷贝数据的长度(字节)

    返回值:成功返回0,失败返回未拷贝的字节的个数

    驱动层:hello.c

    1. #include
    2. #include
    3. #include
    4. #include //添加头文件
    5. #include
    6. #define CNAME "hello"
    7. int major=0;
    8. char kbuf[128]={0}; //存储数据
    9. int dev=0;
    10. ssize_t mycdev_read (struct file *file, char __user *user, size_t size, loff_t * loff)
    11. {
    12.     //printk("this is read\n");
    13.     if(size>sizeof(kbuf))
    14.     {
    15.       size=sizeof(kbuf);
    16.     }
    17.     dev=copy_to_user(user,kbuf,size); //从内核空间到用户空间
    18.     if(dev)
    19.     {
    20.         printk("copy to user err");
    21.         return dev;
    22.     }
    23.     return 0;
    24. }
    25. ssize_t mycdev_write (struct file *file, const char __user *user, size_t size, loff_t *loff)
    26. {  
    27.     //printk("this is write\n");  
    28.     if(size>sizeof(kbuf))
    29.     {
    30.       size=sizeof(kbuf);
    31.     }
    32.     dev=copy_from_user(kbuf,user,size); //从用户空间到内核空间
    33.  return 0;
    34. }
    35. int mycdev_open (struct inode *inode, struct file *file)
    36. {  
    37.     printk("this is open\n");  
    38. return 0;
    39. }
    40. int mycdev_release (struct inode *inode, struct file *file)
    41. {  
    42.     printk("this is close\n");  
    43.     return 0;
    44. }    
    45. const struct file_operations fops={  
    46.     .open=mycdev_open,  
    47.     .read=mycdev_read,  
    48.     .write=mycdev_write,  
    49.     .release=mycdev_release,
    50. };
    51. static int __init hello_init(void)//入口
    52. {
    53.     major=register_chrdev(major,CNAME,&fops);
    54.     if(major<0)  
    55.   {  
    56.     printk("register chrdev error");
    57.    }  
    58. return 0;
    59. }
    60. static void __exit hello_exit(void)//出口
    61. {  
    62.     unregister_chrdev(major,CNAME);
    63. }
    64. module_init(hello_init);//告诉内核驱动的入口
    65. module_exit(hello_exit);//告诉内核驱动的出口
    66. MODULE_LICENSE("GPL");

    应用程序:test.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. char buf[128]="hello world!";
    8. int main(int argc, const char *argv[])
    9. {
    10. int fd;
    11. fd=open("./hello",O_RDWR);
    12. if(fd==-1)
    13. {
    14. perror("open error");
    15. return -1;
    16. }
    17. write(fd,buf,sizeof(buf));
    18. memset(buf,0,sizeof(buf));
    19. read(fd,buf,sizeof(buf));
    20. printf("buf is :%s\n",buf);
    21. close(fd);
    22. return 0;
    23. }

    $make

    $sudo insmod hello.ko

    $gcc test.c

    $./a.out

    $dmesg

     

    3、控制LED灯:

    驱动如何操作寄存器

    rgb_led灯的寄存器是物理地址,在linux内核启动之后,在使用地址的时候操作的全是虚拟地址,需要将物理地址转化为虚拟地址。在驱动代码中操作的虚拟地址就相当于操作实际的物理地址。

    物理地址<------>虚拟地址

    void * ioremap(phys_addr_t offset, unsigned long size)

    功能:将物理地址映射成虚拟地址

    参数:@offset :要映射的物理地址

              @size   :大小(字节)

    返回值:成功返回虚拟地址,失败返回NULL; 

    void iounmap(void  *addr)

    功能:取消映射

    参数: @addr :虚拟地址

    返回值:无

    RGB_led 

    red  :gpioa28

    GPIOXOUT   :控制高低电平的   0xC001A000

    GPIOxOUTENB:输入输出模式    0xC001A004

    GPIOxALTFN1:function寄存器  0xC001A024

    green:gpioe13    0xC001e000

    blue :gpiob12     0xC001b000

     R:GPIOA

    G: GPIOE

     B: GPIOB

    宏定义基地址 ,设置虚拟地址

     将物理地址映射成虚拟地址

     指针类型加1是加的类型大小

     添加头文件:#include

    取消映射:

     

     将hello.ko拷贝到开发板内核文件夹中指针类型加1是加的类型大小

    1. #include <linux/init.h>
    2. #include <linux/module.h>
    3. #include <linux/printk.h>
    4. #include <linux/uaccess.h>
    5. #include <linux/fs.h>
    6. #include <linux/io.h> //添加头文件
    7. int major=0;
    8. #define CNAME "hello"
    9. char kbuf[128]={0};
    10. int dev=0;
    11. #define RED_BASE  0XC001A000
    12. #define BLUE_BASE  0XC001B000
    13. #define GREEN_BASE 0XC001E000
    14. unsigned int *red_base=NULL;
    15. unsigned int *blue_base=NULL;
    16. unsigned int *green_base=NULL;
    17. ssize_t mycdev_read (struct file *file, char __user *user, size_t size, loff_t * loff)
    18. {
    19. //printk("this is read");
    20. if(size>128){
    21.     size=128;
    22. }
    23.     dev=copy_to_user(user,kbuf,size);
    24. if(dev)
    25. {
    26. printk("copy to user errer");
    27. return dev;
    28. }
    29. return 0;
    30. }
    31. ssize_t mycdev_write (struct file *file, const char __user *user, size_t size, loff_t *loff)
    32. {
    33. //printk("this is write");
    34. if(size>128){
    35.     size=128;
    36. }
    37. dev=copy_from_user(kbuf,user,size);
    38. return 0;
    39. }
    40. int mycdev_open (struct inode *inode, struct file *file)
    41. {
    42. printk("this is open");
    43. return 0;
    44. }
    45. int mycdev_release (struct inode *inode, struct file *file)
    46. {
    47. printk("this is close");
    48. return 0;
    49. }
    50. const struct file_operations fops={
    51. .open=mycdev_open,
    52. .read=mycdev_read,
    53. .write=mycdev_write,
    54. .release=mycdev_release,
    55. };
    56. static int __init hello_init(void)//入口
    57. {
    58.   major=register_chrdev(major,CNAME,&fops);
    59. if(major<0)
    60. {
    61. printk("register chrdev error");
    62. }
    63.   red_base=ioremap(RED_BASE,36);
    64. if(red_base==NULL)
    65.    {
    66.   printk("red ioremap error\n");
    67.   return -ENOMEM;
    68. }
    69.   blue_base=ioremap(BLUE_BASE,36);
    70. if(blue_base==NULL)
    71.    {
    72.   printk("blue ioremap error\n");
    73.   return -ENOMEM;
    74. }
    75.     green_base=ioremap(GREEN_BASE,36);
    76. if(green_base==NULL)
    77.    {
    78.   printk("green ioremap error\n");
    79.   return -ENOMEM;
    80. }
    81. *red_base &=~(1<<28);
    82. *(red_base+1) |=1<<28; //指针类型加1是加的类型大小 int占4字节
    83. *(red_base+9) &=~(3<<24);
    84. return 0;
    85. }
    86. static void __exit hello_exit(void)//出口
    87. {
    88. iounmap(green_base);
    89. iounmap(blue_base);
    90. iounmap(red_base);
    91. unregister_chrdev(major,CNAME);
    92. }
    93. module_init(hello_init);//告诉内核驱动的入口
    94. module_exit(hello_exit);//告诉内核驱动的出口
    95. MODULE_LICENSE("GPL");

    Makefile:

    开发板上电测试:

     

    4、应用层控制灯

    1、 驱动层

    判断语句 、宏定义开关

    1. #define RED_ON *red_base |= 1<<28
    2. #define RED_OF *red_base &= ~(1<<28)
    3. ssize_t mycdev_write (struct file *file, const char __user *user, size_t size, loff_t *loff)
    4. {
    5. //printk("this is write");
    6. if(size>128){
    7.     size=128;
    8. }
    9. dev=copy_from_user(kbuf,user,size);
    10. if(kbuf[0]==1)
    11. {
    12. RED_ON;
    13. }
    14. else{
    15. RED_OF;
    16. }
    17. return 0;
    18. }

    2、应用层 

    需要将test.c 在开发板中进行编译,还需要拷贝编译生成的a.out,所以直接在Makefile中添加两行代码:

    1. while(1)
    2.  {
    3.      write(fd,buf,sizeof(buf));
    4. sleep(1);
    5. buf[0]=buf[0]?0:1;
    6.  }

     

  • 相关阅读:
    Servlet Response设置响应数据功能&&完成重定向&&资源路径问题
    RocketMQ源码解读与调试
    Promise封装Ajax请求
    微信小程序之console.log()使用
    【LeetCode75】第六十二题 多米诺和托米诺平铺
    电视机@2022:降价、焦虑与机遇
    10月26日,每日信息差
    使用 MoveIt 控制自己的真实机械臂【3】——封装 ROS 驱动
    为什么建议MySQL在2000W条左右记录分表
    动图GIF图片怎么制作?教你一键搞定
  • 原文地址:https://blog.csdn.net/m0_68672255/article/details/131080910