Firefly开源社区

ROC-RK3308-CC开发实例总结--PS2 Joystick摇杆模块

200

积分

0

威望

0

贡献

技术达人

Rank: 2

积分
200
发表于 2019-4-8 17:58:01     
本帖最后由 Demon 于 2019-4-8 17:58 编辑

    最近本人又调试了一款比较有趣的传感器模块--PS2  Joystick摇杆模块。下面将本人的测试方法分享于大家,下文仅个人见解,若有不足之处,望指出,不胜感激。
一、模块介绍
    PS2双轴游戏摇杆模块采用了PS2游戏手柄上优质金属按键摇杆电位器,模块集成电源指示灯,可以显示工作状态;坐标标识符清晰简明、定位准确。
模块外观.png
    我们先来看下它的工作原理吧,那样我们也知道它里面到底是怎么回事,这对我们对它的使用很有帮助,下面是模块的功能示意图:
功能示意图.png
     由上图可见的,模块设有二路模拟输出和一路数字输出接口,输出值分别对应(X,Y)双轴偏移量,其类型为模拟量;另一数字表示用户是否在Z轴按下,其类型为数字开关量。通过控制器编程,传感器扩展板插接,完成具有创意性的互动作品或者游戏。
game.jpeg
game1.jpeg
二、代码简述
     按照惯例,在了解模块后,先在kernel/arch/arm64/boot/dts/rockchip/rk3308-firefly.dtsi设备树中添加设备的节点信息。
  1.     PS2_test@ff1e0000{
  2.             compatible = "ps2 joystick";
  3.             clocks = <&cru SCLK_SARADC>;
  4.             lock-names = "saradc";
  5.             Z-axis = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>;
  6.             io-channels = <&saradc 0> , <&saradc 2>;
  7.             io-channel-names = "X-axis" , "Y-axis";
  8.             status = "okay";                              
  9.     };
复制代码
   代码中使用了ADC通道0与ADC通道2,还有一个GPIO口。所使用的ADC时钟为saradc。下面将进行驱动代码的编写:首先在kernel/drivers/adc/下创建一个对应的文件夹,加入Kconfig、Makefile、ps2.c其中。
  1. static struct file_operations ps2_fops = {
  2.         .owner = THIS_MODULE,
  3.         .open  = ps2_open,
  4.         .release = ps2_release,
  5.         .read  = ps2_read,
  6. };

  7. static struct miscdevice ps2_miscdev = {
  8.         .minor = MISC_DYNAMIC_MINOR,
  9.         .name  = "PS2",
  10.         .fops  = &ps2_fops,
  11. };

  12. static const struct of_device_id ps2_match[] = {
  13.     { .compatible = "ps2 joystick" },
  14.     {},
  15. };

  16. static struct platform_driver ps2_driver = {
  17.     .probe = ps2_probe,
  18.     .remove = ps2_remove,
  19.     .driver = {
  20.         .owner = THIS_MODULE,
  21.         .name = "PS2 Game joystick module",
  22.         .of_match_table = ps2_match,
  23.     }
  24. };

  25. static int ps2_init(void)
  26. {
  27.     adc_clock = clk_get(NULL, "saradc");
  28.     if(!adc_clock) {
  29.         return -ENOENT;
  30.     }
  31.     clk_enable(adc_clock);
  32.     platform_driver_register(&ps2_driver);
  33.     return misc_register(&ps2_miscdev);
  34. }
复制代码
    在此就不对驱动的框架进行赘述,在初始化函数中将驱动申请为杂项设备,还有相关的设备信息。下面将对probe函数进行解析:
  1. static int ps2_probe(struct platform_device *pdev)
  2. {
  3.     enum of_gpio_flags z_flag;

  4.     devs = pdev;
  5.     ps2 = kmalloc(sizeof(struct PS2), GFP_KERNEL);
  6.     if(!ps2){
  7.         printk("ps2 kmalloc memory err!!!\n");
  8.         return -ENODEV;
  9.     }

  10.     z_axis_gpio = of_get_named_gpio_flags(pdev->dev.of_node, "Z-axis", 0, &z_flag);
  11.     if(!gpio_is_valid(z_axis_gpio)) {
  12.         printk("z-axis is invalid!\n");
  13.         return -ENODEV;
  14.     }
  15.     gpio_direction_input(z_axis_gpio);

  16.     chan0 = iio_channel_get(&(pdev->dev), "X-axis");
  17.     if(IS_ERR(chan0)){
  18.         return -1;
  19.     }
  20.     chan2 = iio_channel_get(&(pdev->dev), "Y-axis");
  21.     if(IS_ERR(chan2)){
  22.         return -1;
  23.     }
  24.     return 0;
  25. }
复制代码
   其中需要一提的是
iio_channel_get()函数,功能作用为:获取 iio 通道描述。参数为:设备描述指针、所用通道描述指针。of_get_named_gpio_flags()功能为:获取GPIO信息。gpio_is_valid()功能为:判断GPIO是否合法。下面为ps2_read()函数:
  1. ssize_t ps2_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos)
  2. {
  3.     int ret;
  4.     char data[20] = {0};

  5.     ps2_get_value();
  6.     sprintf(data, "%d,%d,%d",ps2->x_axis,ps2->y_axis,ps2->z_axis);
  7.     ret = copy_to_user(buf, data, sizeof(data));
  8.     return ret;
  9. }
复制代码
   在read()函数中调用的
ps2_get_value()是获取所需值的函数。具体如下:
  1. int ps2_get_value(void)
  2. {
  3.     int ret;

  4.     ret = iio_read_channel_raw(chan0, &(ps2->x_axis));
  5.     if(ret < 0 || ret > 4) {
  6.         printk("read channel() erro!!!");
  7.         return ret;
  8.     }
  9.     ps2->x_axis = (STANDARD_VOL * ps2->x_axis) / CONVERSION_NUM;
  10.    
  11.     ret = iio_read_channel_raw(chan2, &(ps2->y_axis));
  12.     if(ret < 0 || ret > 4){
  13.         printk("read channel() erro!!!");
  14.         return ret;
  15.     }
  16.     ps2->y_axis = (STANDARD_VOL * ps2->y_axis) / CONVERSION_NUM;

  17.     ps2->z_axis = gpio_get_value(z_axis_gpio);
  18.     return 0;
  19. }
复制代码
   其中
iio_read_channel_raw()函数的功能为:读取 chan 通道 AD 采集的原始数据。参数为:通道指针、数据存放指针。此处的STANDARD_VOL与CONVERSION_NUM分别表示标准电压、转换位数。关于ADC的使用方法,可以参照firefly官方wikADC使用
    gpio_get_value()函数的功能为:获取GPIO的IO值。代码写到此处,驱动算是基本已经完成,对其进行编译,生成固件,烧入开发板。以下为应用层测试程序的编写:
  1. int main(int argc, char argv[])
  2. {
  3.     char data[20] = {0};
  4.     int fd,ret;
  5.     int ps2[3];
  6.     char tmp = ',';
  7.     char *flag[5] = {0};
  8.         
  9.     fd = open("/dev/PS2", O_RDONLY);
  10.     if(fd < 0){
  11.         printf("open /dec/PS2 faild!!\n");
  12.         return -1;
  13.     }
  14.         
  15.     while(1)
  16.     {
  17.         ret = read(fd, buff, sizeof(data));
  18.         if(ret < 0){
  19.                 printf("read fd faild!!\n");
  20.                 return -1;
  21.         }

  22.         flag = strtok(data, &tmp);
  23.         if(flag)
  24.                 ps2[0] = atoi(flag);
  25.         flag = strtok(NULL, &tmp);
  26.         if(flag)
  27.                 ps2[1] = atoi(flag);
  28.         flag = strtok(NULL, &tmp);
  29.         if(flag)
  30.                 ps2[2] = atoi(flag);
  31.         
  32.         printf("x: %d, y: %d, z:%d", ps2[0], ps2[1], ps2[2]);
  33.         bzero(data, 20);
  34.         sleep(1);
  35.     }
  36.     return 0;
  37. }
复制代码
   测试程序较为简单,访问读取/dev/PS2数据即可。再使用strtok()函数分割data,得到每个轴的数据。完成之后,使用指定的交叉编译链进行编译,将可执行文件传输至开发板中。

三、效果展示
    一切准备工作处理完成之后,即可执行程序,查看代码效果!!
效果.png

    (此文仅个人见解,若有不足之处,望指出,不胜感激)



ps2.tar.gz

830.65 KB, 下载次数: 5, 下载积分: 灯泡 -1 , 经验 -1

售价: 3 灯泡  [记录]

相关代码及资料

回复

使用道具 举报

10

积分

0

威望

0

贡献

技术小白

积分
10
发表于 2019-4-17 21:42:31     
good
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

友情链接 : 爱板网 电子发烧友论坛 云汉电子社区 粤ICP备14022046号-2
快速回复 返回顶部 返回列表