微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > FPGA,CPLD和ASIC > 第三篇?Rico Board使用 hrt高精度时钟驱动舵机

第三篇?Rico Board使用 hrt高精度时钟驱动舵机

时间:10-02 整理:3721RD 点击:

首先科普一下舵机驱动的要求:


既然周期是20ms,脉宽处在0.5ms~2.5ms。所以,普通的内核定时器就不能满足要求了,就要用到高精度定时器。我主要参考的内容如下:




这都是摘自宋宝华的《Linux设备驱动开发详解基于最新的Linux4.0内核》。

我的代码如下:

  1. /**
  2. * [url=home.php?mod=space&uid=1455510]@file[/url]   motor.c
  3. * [url=home.php?mod=space&uid=40524]@author[/url] yang yongsheng
  4. * @date   2016.11.2
  5. */

  6. #include
  7. #include
  8. #include
  9. #include                  // Required for the GPIO functions
  10. #include
  11. #include
  12. #include
  13. #include
  14. #include
  15. #include
  16. #include
  17. #include
  18. #include

  19. #define NAME_LEN 16

  20. /*gpio5_8*/
  21. //#define MOTOR_GPIO_NUM 168

  22. /*gpio5_9*/
  23. #define LED_GPIO_NUM 169


  24. #define MOTOR_NUM 1
  25. #define MOTOR_NAME "rico_lan motor"

  26. #define MOTOR_MAJOR 0
  27. static int motor_major = MOTOR_MAJOR;
  28. static dev_t motor_dev_num;
  29. int motor_gpio[MOTOR_NUM] = {113};

  30. MODULE_LICENSE("GPL");
  31. MODULE_AUTHOR("yang yongsheng ");
  32. MODULE_VERSION("0.1");

  33. struct motor_dev{
  34.         struct cdev cdev;
  35.         int num;
  36.         unsigned int pwm_value_us;
  37.         struct hrtimer hrt;
  38.         char name[NAME_LEN];
  39.         bool pwm_state;
  40. } *motor_devp;

  41. struct class *motor_class;

  42. static enum hrtimer_restart motor_hrtimer_callback(struct hrtimer *hrt)
  43. {
  44.         struct motor_dev *devp_tmp;
  45.         devp_tmp = container_of(hrt, struct motor_dev, hrt);
  46.         devp_tmp->pwm_state = !devp_tmp->pwm_state;
  47.         gpio_set_value(devp_tmp->num, devp_tmp->pwm_state);
  48.         if(devp_tmp->pwm_state == false)
  49.         hrtimer_forward_now(hrt, ns_to_ktime((20000-devp_tmp->pwm_value_us)*1000));//T=20,000ns
  50.         else if(devp_tmp->pwm_state == true)
  51.         hrtimer_forward_now(hrt, ns_to_ktime((devp_tmp->pwm_value_us) * 1000));//T=20,000ns
  52.         //printk(KERN_INFO "hrt time is %d",devp_tmp->pwm_value_us);
  53.         return HRTIMER_RESTART;
  54. }

  55. int motor_open (struct inode *inode, struct file *filp)
  56. {
  57.         struct motor_dev *devp_tmp;
  58.         devp_tmp = container_of(inode->i_cdev, struct motor_dev, cdev);
  59.         filp->private_data = devp_tmp;
  60.         hrtimer_init(&devp_tmp->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
  61.         devp_tmp->hrt.function = motor_hrtimer_callback;
  62.         gpio_set_value(devp_tmp->num, devp_tmp->pwm_state);
  63.         hrtimer_start(&devp_tmp->hrt, ns_to_ktime(devp_tmp->pwm_value_us), HRTIMER_MODE_REL);
  64.         printk(KERN_INFO "motor_open.\n");
  65.         return 0;
  66. }

  67. int motor_release (struct inode *inode, struct file *filp)
  68. {
  69.         struct motor_dev *devp_tmp;
  70.         devp_tmp = filp->private_data;
  71.         printk(KERN_INFO "motor_release and pwm_value_us is %d.\n", devp_tmp->pwm_value_us);
  72.         return 0;        
  73. }

  74. ssize_t motor_write (struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
  75. {
  76.         char pwm_value_tmp[8];
  77.         struct motor_dev *motor_devp_tmp = filp->private_data;
  78.         copy_from_user(pwm_value_tmp, buf, sizeof(buf));        
  79.         gpio_set_value(LED_GPIO_NUM        , motor_devp_tmp->pwm_state);        
  80.         motor_devp_tmp->pwm_value_us = simple_strtoul(pwm_value_tmp, NULL, 10);
  81.         if(motor_devp_tmp->pwm_value_us > 2500)
  82.                 motor_devp_tmp->pwm_value_us = 2500;
  83.         else if(motor_devp_tmp->pwm_value_us pwm_value_us = 500;
  84.         printk(KERN_INFO "pwm_value is %d.\n", motor_devp_tmp->pwm_value_us);
  85.         printk(KERN_INFO "motor_write.\n");
  86.         return count;
  87. }

  88. static struct file_operations motor_fops =
  89. {
  90.         .owner = THIS_MODULE,
  91.         .open = motor_open,
  92.         .release = motor_release,
  93.         .write = motor_write,
  94. };

  95. int motor_setup(struct motor_dev *devp, int min, int num)
  96. {
  97.         int ret = 0;
  98.         sprintf(devp->name, "motor%d", min);        
  99.         if(!gpio_is_valid(num))
  100.         {
  101.                 printk(KERN_INFO "invalid motor%d_gpio_num:%d.\n",min, num);
  102.                 return -ENODEV;
  103.         }
  104.         else
  105.         {
  106.         gpio_request(num, "sysfs");
  107.         gpio_direction_output(num, false);
  108.         gpio_export(num, false);        /*cause GPIO_NUM to appear in /sys/class/gpio*/
  109.         }
  110.         devp->num = num;
  111.         devp->pwm_value_us = 1500;//500~2500 us
  112.         devp->pwm_state = true;
  113.         devp->cdev.owner = THIS_MODULE;
  114.         cdev_init(&devp->cdev, &motor_fops);
  115.         ret = cdev_add(&devp->cdev, MKDEV(motor_major, min), 1);
  116.         if(ret)
  117.         {
  118.                 printk(KERN_INFO "error %d adding motor%d.\n", ret, min);
  119.         }
  120.         else
  121.         {
  122.                 device_create(motor_class, NULL, MKDEV(motor_major, min), NULL,devp->name);
  123.         }
  124.         return 0;
  125. }

  126. int __init motor_init(void)
  127. {
  128.         int ret,i;
  129.         printk(KERN_INFO "motor_init begin.\n");
  130.         if(!gpio_is_valid(LED_GPIO_NUM))
  131.         {
  132.                 printk(KERN_INFO "invalid LED_GPIO_NUM:%d.\n",LED_GPIO_NUM);
  133.                 return -ENODEV;
  134.         }

  135.         motor_devp = kzalloc(sizeof(struct motor_dev) * MOTOR_NUM, GFP_KERNEL);
  136.         if(IS_ERR(motor_devp))
  137.         {
  138.                 printk(KERN_INFO "no space for motor_dev.\n");
  139.                 return -ENOMEM;
  140.         }

  141.         motor_class = class_create(THIS_MODULE, MOTOR_NAME);

  142.         if(motor_major == 0)
  143.         {
  144.                 ret = alloc_chrdev_region(&motor_dev_num, 0, MOTOR_NUM, MOTOR_NAME);
  145.                 motor_major = MAJOR(motor_dev_num);
  146.         }
  147.         else
  148.         {
  149.                 motor_dev_num = MKDEV(motor_major, 0);
  150.                 ret = register_chrdev_region(motor_dev_num, MOTOR_NUM, MOTOR_NAME);
  151.         }
  152.         if(ret hrt);
  153.                 gpio_unexport((motor_devp+i)->num);
  154.                 gpio_free((motor_devp+i)->num);
  155.                 device_destroy(motor_class,MKDEV(motor_major,i));
  156.                 cdev_del(&(motor_devp+i)->cdev);
  157.                 unregister_chrdev_region(MKDEV(motor_major, i), 1);
  158.                 kfree(motor_devp+i);
  159.         }
  160.         gpio_unexport(LED_GPIO_NUM);
  161.         gpio_free(LED_GPIO_NUM);
  162.         class_destroy(motor_class);        
  163.         
  164.         printk(KERN_INFO "motor_exit finished.\n");
  165. }

  166. module_init(motor_init);
  167. module_exit(motor_exit);

复制代码


实现的内容:


往字符设备/dev/motor0 写入脉宽的时间(500ns~2500ns)可以自动修正超界的脉宽值。可以控制舵机旋转角度在(0~180)。

驱动舵机的GPIO为GPIO3_17 (也就是113号引脚,程序里面有体现)。

现场视频(用户程序控制输出脉宽每1s,增加50us),已经上传到了优酷:

1。示波器显示,

http://v.youku.com/v_show/id_XMTc4NTUyMTM4NA==.html

2。现场舵机转动:

http://v.youku.com/v_show/id_XMTc4NTUyMjIzNg==.html

下面记录下我的整个实验过程:


1.安装编译好的模块,自动创建设备节点/dev/motor0


2.查看默认输出的脉宽1500ns:


关键代码如下:


查看波形,脉宽1.52ms:


周期20ms:


3.命令行下,手动更改脉宽到2000ns:


脉宽显示2ms


4.放上几张现场图片,挺乱的,太懒了我,东西都堆在一起了。




这次主要学到了两点

(1)module_init和module_exit用大写时就出错了,要记得用小写。


(2)知道了高精度定时器hrt的使用方法。




这个 ,大材小用了吧,即使RicoBoard能输出超精密的PWM波,舵机也不见得能分辨的过来啊

那还能有什么好办法实现啊?

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top