• 华清11.16


    首先在头函数led.h中声明gpio结构体和gpioe,gpiof,rcc的物理地址。

    1. #ifndef __LED_H__
    2. #define __LED_H__
    3. typedef struct
    4. {
    5. volatile unsigned int MODER;
    6. volatile unsigned int OTYPER;
    7. volatile unsigned int OSPEEDR;
    8. volatile unsigned int PUPDR;
    9. volatile unsigned int IDR;
    10. volatile unsigned int ODR;
    11. }gpio_t;
    12. #define WL_GPIOE 0x50006000
    13. #define WL_GPIOF 0x50007000
    14. #define WL_RCC 0x50000a28
    15. #endif

     在驱动程序中,定义gpio结构体类型的两个指针接受物理地址映射的虚拟地址,然后通过结构体来寻找对应的寄存器,而不用手动添加偏移量。

    在init函数中申请驱动设备号,映射地址,使能并初始化灯。在write函数中接受来自用户程序的值,存入kbuf中。kbuf[0]中的值用以确定指定操作的小灯编号,kbud[1]中的值确定亮灭操作。

    最后在exit函数中接触映射并注销字符设备驱动。

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include"led.h"
    7. #define CNAME "myled"
    8. int major;
    9. volatile int* RCC=NULL;
    10. volatile gpio_t* GPIOE=NULL;
    11. volatile gpio_t* GPIOF=NULL;
    12. char kbuf[128]={0};
    13. int mydev_open (struct inode *inode, struct file *file)
    14. {
    15. return 0;
    16. }
    17. ssize_t mydev_read (struct file *file, char __user *ubuf, size_t size, loff_t *loffs)
    18. {
    19. int res;
    20. if(size>sizeof(kbuf))
    21. size=sizeof(kbuf);
    22. res=copy_to_user(ubuf,kbuf,size);
    23. if(res)
    24. {
    25. printk("copy to user error");
    26. return EIO;
    27. }
    28. return size;
    29. }
    30. ssize_t mydev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loffs)
    31. {
    32. int res;
    33. if(size > sizeof(kbuf))
    34. size=sizeof(kbuf);
    35. res=copy_from_user(kbuf,ubuf,size);
    36. if(res)
    37. {
    38. printk("copy from user error\n");
    39. return EIO;
    40. }
    41. printk("copy over , kbuf=%s\n",kbuf);
    42. //接收后亮灯逻辑,kbuf[0]表示灯,kbuf[1]表示亮灭
    43. if(kbuf[0]=='1')
    44. {
    45. if(kbuf[1]=='1')
    46. {
    47. GPIOE->ODR|=(0x1<<10);
    48. }
    49. else if(kbuf[1]=='0')
    50. {
    51. GPIOE->ODR&=(~(0x1<<10));
    52. }
    53. }
    54. if(kbuf[0]=='2')
    55. {
    56. if(kbuf[1]=='1')
    57. {
    58. GPIOF->ODR|=(0x1<<10);
    59. }
    60. else if(kbuf[1]=='0')
    61. {
    62. GPIOF->ODR&=(~(0x1<<10));
    63. }
    64. }
    65. if(kbuf[0]=='3')
    66. {
    67. if(kbuf[1]=='1')
    68. {
    69. GPIOE->ODR|=(0x1<<8);
    70. }
    71. else if(kbuf[1]=='0')
    72. {
    73. GPIOE->ODR&=(~(0x1<<8));
    74. }
    75. }
    76. return size;
    77. }
    78. int mydev_close (struct inode *inode, struct file *file)
    79. {
    80. return 0;
    81. }
    82. const struct file_operations fops= {
    83. .open=mydev_open,
    84. .read=mydev_read,
    85. .write=mydev_write,
    86. .release=mydev_close,
    87. };
    88. static int __init demo_init(void)
    89. {
    90. major=register_chrdev(major,CNAME,&fops);
    91. if(major<0)
    92. {
    93. printk("register_chrdev error\n");
    94. return major;
    95. }
    96. printk("major=%d\n",major);
    97. //RCC映射
    98. RCC=ioremap(WL_RCC,4);
    99. if(NULL==RCC)
    100. {
    101. printk("rcc ioremap error\n");
    102. return ENOMEM;
    103. }
    104. //GPIOE映射
    105. GPIOE=ioremap(WL_GPIOE,sizeof(gpio_t));
    106. if(NULL==GPIOE)
    107. {
    108. printk("gpioE ioremap error\n");
    109. return ENOMEM;
    110. }
    111. //GPIOF映射
    112. GPIOF=ioremap(WL_GPIOF,sizeof(gpio_t));
    113. if(NULL==GPIOF)
    114. {
    115. printk("gpioF ioremap error\n");
    116. return ENOMEM;
    117. }
    118. //初始化灯
    119. *RCC|=(0x1<<4);
    120. *RCC|=(0x1<<5);
    121. GPIOE->MODER&=(~(0x3<<20));
    122. GPIOE->MODER|=(0x1<<20);
    123. GPIOF->MODER&=(~(0x3<<20));
    124. GPIOF->MODER|=(0x1<<20);
    125. GPIOE->MODER&=(~(0x3<<16));
    126. GPIOE->MODER|=(0x1<<16);
    127. return 0;
    128. }
    129. static void __exit demo_exit(void)
    130. {
    131. //解除映射
    132. iounmap(GPIOE);
    133. iounmap(GPIOF);
    134. iounmap(RCC);
    135. unregister_chrdev(major,CNAME);
    136. }
    137. module_init(demo_init);
    138. module_exit(demo_exit);
    139. MODULE_LICENSE("GPL");

     在用户程序启动前先下载(insmod)驱动程序(.ko文件)并在/dev/下添加申请的驱动。

    在调用open,write,read,等函数时会调用驱动代码中对应的函数。

    通过在死循环中写入1灯亮,1灯灭,2灯亮,2灯灭,3灯亮,3灯灭的循环,并在每步操作后sleep休眠来观察现象。

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. char buf[128]={""};
    9. int main(int argc, char const *argv[])
    10. {
    11. int fd=-1;
    12. fd=open("/dev/myled",O_RDWR);
    13. if(-1==fd)
    14. {
    15. perror("open error");
    16. return -1;
    17. }
    18. while (1)
    19. {
    20. buf[0]='1';
    21. buf[1]='1';
    22. write(fd,buf,sizeof(buf));
    23. sleep(1);
    24. buf[0]='1';
    25. buf[1]='0';
    26. write(fd,buf,sizeof(buf));
    27. sleep(1);
    28. buf[0]='2';
    29. buf[1]='1';
    30. write(fd,buf,sizeof(buf));
    31. sleep(1);
    32. buf[0]='2';
    33. buf[1]='0';
    34. write(fd,buf,sizeof(buf));
    35. sleep(1);
    36. buf[0]='3';
    37. buf[1]='1';
    38. write(fd,buf,sizeof(buf));
    39. sleep(1);
    40. buf[0]='3';
    41. buf[1]='0';
    42. write(fd,buf,sizeof(buf));
    43. sleep(1);
    44. }
    45. close(fd);
    46. return 0;
    47. }

    流水灯

  • 相关阅读:
    String 为什么不可变?不可变有什么好处?
    设计模式:享元模式(C++实现)
    吴恩达机器学习笔记 二十二 机器学习项目的完整周期 公正、偏见和伦理
    LoRA大模型加速微调和训练算法解读
    C#变量命名规则(命名规范)
    IDM的实用功能
    计算机网络---数据链路层HDLC协议
    跨域请求的方法
    数据库面试题+解析
    【舞台灯方案】LED驱动恒流芯片pwm深度调光APS54085降压IC
  • 原文地址:https://blog.csdn.net/qq_49971476/article/details/127890999