关闭

基于树莓派3B+,一个可以遥控的小车(一)

标签: Linux驱动树莓派GPIO小车控制
2431人阅读 评论(0) 收藏 举报
分类:

一、树莓派端口驱动

用树莓派的8个端口驱动小车的四个电机(一个电机两根线),首先修改DTS(Device Tree Source)文件(bcm2708_common.dtsi,bcm2710-rpi-3-b.dts),在对应的DTS文件中加入car节点,该节点定义了具体使用树莓派的哪8个端口和一些状态信息。(该节点定义基于GPIO子系统),具体如下:





在修改好DTS文件后,可以重编内核(也可以选择只编译DTS),接下来是驱动程序:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <linux/fs.h>

#define CAR_STOP 0
#define CAR_FORWORLD 1
#define CAR_BACK 2
#define CAR_TURN_LIGHT 3
#define CAR_TURN_RIGHT 4

struct wheel {
	unsigned int	F_pin;
	unsigned int	B_pin;
	bool forworld;
	unsigned int	scl_is_output_only:1;
};

enum car_direction {
        CAR_DIR_STOP     = 0,
        CAR_DIR_FORWORLD = 1,
        CAR_DIR_BACK     = 2,
        CAR_DIR_RIGHT    = 3,
        CAR_DIR_LIGHT    = 4,
		CAR_DIR_NUM      = 5
};


struct bcm2835_car {
        enum car_direction car_dir;
        struct wheel car_wheel[4];
};

struct car_gpio_platform_data {
	struct miscdevice car_ctrl_miscdev;
	enum car_direction car_dir;
	struct wheel car_wheels[4];
	struct device car_dev;
};

/* set the direction of the pin */
static void car_gpio_set_direction(void *data, int num, int state)
{
	struct car_gpio_platform_data *pdata = data;
	struct wheel *car_wheel = pdata->car_wheels;

	if (state){
		gpio_direction_input(car_wheel[num].F_pin);
		gpio_direction_input(car_wheel[num].B_pin);
	}else{
		gpio_direction_output(car_wheel[num].F_pin, 0);
		gpio_direction_output(car_wheel[num].B_pin, 0);
	}
}


static void car_gpio_setwheel_direction(void *data, int num, int forworld)
{
	struct car_gpio_platform_data *pdata = data;
	struct wheel *car_wheel = pdata->car_wheels;
	
	if (1 == forworld){/*forworld*/
		gpio_set_value(car_wheel[num].F_pin, 1);
		gpio_set_value(car_wheel[num].B_pin, 0);
	}else if(-1 == forworld){ /*back*/
		gpio_set_value(car_wheel[num].F_pin, 0);
		gpio_set_value(car_wheel[num].B_pin, 1);
	}else { /*stop*/
		gpio_set_value(car_wheel[num].F_pin, 0);
                gpio_set_value(car_wheel[num].B_pin, 0);
	}
	
}

static void car_gpio_run_status(void *data, enum car_direction dir)
{
        struct car_gpio_platform_data *pdata = data;

	switch(dir){
	case CAR_DIR_STOP:
		printk(KERN_DEBUG "CAR_DIR_STOP:\n");
		car_gpio_setwheel_direction(pdata,0,0);
		car_gpio_setwheel_direction(pdata,1,0);
		car_gpio_setwheel_direction(pdata,2,0);
		car_gpio_setwheel_direction(pdata,3,0);

		break;
	case CAR_DIR_FORWORLD:
		printk(KERN_DEBUG "CAR_DIR_FORWORLD:\n");
		car_gpio_setwheel_direction(pdata,0,1);
		car_gpio_setwheel_direction(pdata,1,1);
		car_gpio_setwheel_direction(pdata,2,1);
		car_gpio_setwheel_direction(pdata,3,1);
		break;
	case CAR_DIR_BACK:
		printk(KERN_DEBUG "CAR_DIR_BACK:\n");
		car_gpio_setwheel_direction(pdata,0,-1);
		car_gpio_setwheel_direction(pdata,1,-1);
		car_gpio_setwheel_direction(pdata,2,-1);
		car_gpio_setwheel_direction(pdata,3,-1);
		break;
	case CAR_DIR_RIGHT:
		printk(KERN_DEBUG "CAR_DIR_RIGHT:\n");

		car_gpio_setwheel_direction(pdata,0,1);
		car_gpio_setwheel_direction(pdata,1,-1);
		car_gpio_setwheel_direction(pdata,2,1);
		car_gpio_setwheel_direction(pdata,3,-1);
		break;
	case CAR_DIR_LIGHT:
		printk(KERN_DEBUG "CAR_DIR_LIGHT:\n");
		car_gpio_setwheel_direction(pdata,0,-1);
		car_gpio_setwheel_direction(pdata,1,1);
		car_gpio_setwheel_direction(pdata,2,-1);
		car_gpio_setwheel_direction(pdata,3,1);
		break;

	}
}


static int of_car_gpio_get_pins(struct device_node *np,
				struct wheel *wheel_pin)
{
	int i=0;
	if (of_gpio_count(np) < 8)
		return -ENODEV;
	for(i=0; i<4; i++){
		wheel_pin[i].F_pin = of_get_gpio(np, i*2);
		wheel_pin[i].B_pin = of_get_gpio(np, i*2 + 1);
		printk(KERN_INFO "wheel_pin[i].F_pin = %d wheel_pin[i].B_pin=%d \n",wheel_pin[i].F_pin,wheel_pin[i].B_pin);
		if (wheel_pin[i].F_pin == -EPROBE_DEFER || wheel_pin[i].B_pin == -EPROBE_DEFER)
		return -EPROBE_DEFER;

	if (!gpio_is_valid(wheel_pin[i].F_pin) || !gpio_is_valid(wheel_pin[i].B_pin)) {
		pr_err("%s: invalid GPIO pins, F_pin=%d/B_pin=%d\n",
		       np->full_name, wheel_pin[i].F_pin, wheel_pin[i].F_pin);
		return -ENODEV;
	}
}
	return 0;
}

static int car_ctrl_open(struct inode *inode, struct file *file)
{
	struct car_gpio_platform_data *data;
	struct miscdevice *miscdev = file->private_data;

	data = container_of(miscdev,struct car_gpio_platform_data, car_ctrl_miscdev);

	file->private_data = data; /*Now, the private_data point to platform device driver data*/

	return 0;
}
static int car_ctrl_release(struct inode *inode, struct file *file)
{
	struct car_gpio_platform_data *data = file->private_data;

	kfree(data);
	return 0;
}

static long car_ctrl_ioctl(struct file *file, unsigned int cmd,
			    unsigned long arg)
{

	struct car_gpio_platform_data *car_data = file->private_data; 
	if(cmd > CAR_DIR_STOP && cmd < CAR_DIR_NUM)
		car_gpio_run_status(car_data,cmd);

	#if 0
	switch (cmd) {
	case CAR_FORWORLD:
		printk(KERN_INFO "CAR_FORWORLD \n ");
		car_gpio_run_status();
		break;

	case CAR_BACK:
		printk(KERN_INFO "CAR_BACK \n ");
		break;

	case CAR_TURN_LIGHT:
		printk(KERN_INFO "CAR_TURN_LIGHT \n ");
		break;

	case CAR_TURN_RIGHT:
		printk(KERN_INFO "CAR_TURN_RIGHT \n ");
		break;

	case CAR_STOP:
		printk(KERN_INFO "CAR_STOP \n ");
		break;

	default:
		err = -ENOTTY;
		break;
	}
#endif
	return 1;

}

static const struct file_operations car_ctrl_fops = {
	.owner		= THIS_MODULE,
	.unlocked_ioctl	= car_ctrl_ioctl,
	.open		= car_ctrl_open,
	.release	= car_ctrl_release,
};

static int car_gpio_probe(struct platform_device *pdev)
{
	
	struct car_gpio_platform_data *pdata;
	struct wheel *car_wheel_temp;
	struct miscdevice *car_misc;
	unsigned int i;
	char name[20];
	int ret;
	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);	
	car_wheel_temp = pdata->car_wheels;
	car_misc = &pdata->car_ctrl_miscdev;	
	printk(KERN_INFO "car_gpio_probe !!!!!!!!!!!!!!!!!!!!!!!!!\n");
	/* First get the GPIO pins; if it fails, we'll defer the probe. */
	if (pdev->dev.of_node){
		ret = of_car_gpio_get_pins(pdev->dev.of_node,
					   car_wheel_temp);
		if (ret)
			return ret;
	} else {
			return -ENXIO;
	}

	for(i=0; i<4; i++) {
		printk(KERN_INFO "car_wheel_temp[i].F_pin = %d\n",car_wheel_temp[i].F_pin);	
		sprintf(name,"CAR_wheel_%d_F",i);
		ret = devm_gpio_request(&pdev->dev, car_wheel_temp[i].F_pin, name);
			if (ret) {
				if (ret == -EINVAL)
					ret = -EPROBE_DEFER;	/* Try again later */
			return ret;
			}
		printk(KERN_INFO "devm_gpio_request name = %s\n",name);	
		sprintf(name,"CAR_wheel_%d_B",i);
		ret = devm_gpio_request(&pdev->dev, car_wheel_temp[i].B_pin, name);
			if (ret) {
				if (ret == -EINVAL)
					ret = -EPROBE_DEFER;	/* Try again later */
			return ret;
			}
		printk(KERN_INFO "devm_gpio_request name = %s\n",name);	
	}


	for(i=0;i<4;i++)
		car_gpio_set_direction(pdata,i,0);
	//准备组册设备
	car_misc->minor = MISC_DYNAMIC_MINOR;
	car_misc->name = "car";
	car_misc->fops = &car_ctrl_fops;
	car_misc->parent = &pdev->dev;

	ret = misc_register(car_misc);
	if (ret) {
		dev_err(&pdev->dev,
			"unable to register misc device, err=%d\n", ret);
		return 0;
	}

	platform_set_drvdata(pdev, pdata);

	dev_info(&pdev->dev, "Finish car probe successfully \n");

	return 0;
}

static int car_ctrl_remove(struct platform_device *pdev)
{
	struct car_gpio_platform_data  *car_data;

	car_data = platform_get_drvdata(pdev);
	if (!car_data)
		return -ENODATA;

	misc_deregister(&car_data->car_ctrl_miscdev);
	return 0;
}
static int car_gpio_remove(struct platform_device *pdev)
{
	car_ctrl_remove(pdev);
	return 0;
}

#if defined(CONFIG_OF)
static const struct of_device_id car_gpio_dt_ids[] = {
	{ .compatible = "car-gpio", },
	{ /*  */ }
};

MODULE_DEVICE_TABLE(of, car_gpio_dt_ids);
#endif

static struct platform_driver car_gpio_driver = {
	.driver		= {
		.name	= "car-gpio",
		.of_match_table	= of_match_ptr(car_gpio_dt_ids),
	},
	.probe		= car_gpio_probe,
	.remove		= car_gpio_remove,
};

static int __init car_gpio_init(void)
{
	int ret;

	printk(KERN_INFO "Entry car_gpio_init !!!!!!!!!!!!!!!!!!!!!!\n");
	ret = platform_driver_register(&car_gpio_driver);
	if (ret)
		printk(KERN_ERR "car-gpio: probe failed: %d\n", ret);

	return ret;
}
module_init(car_gpio_init);

static void __exit car_gpio_exit(void)
{
	platform_driver_unregister(&car_gpio_driver);
}
module_exit(car_gpio_exit);

MODULE_DESCRIPTION("Platform-independent car driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:car-gpio");

二、驱动代码和一般驱动代码的框架一样,主要在ioctl上实现自己的逻辑为应用程序提供接口。再接着是应用程序测试。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "api_car.h"
int main(void)
{
    int fd_car;
	char chr;	
    int delay = 20;
    int mode=0;
    fd_car = api_car_open();
    
    if(fd_car < 0){
        printf("fd_car open faicar!\n");
        return -1;
    }
    
    while(delay--)
{

printf("Please input car mode [0~4]: ");
scanf("%d",&chr);

printf("\nmode = %d\n",chr);
    api_car_ioctl(fd_car, chr);

} 
   printf("Hello, Water CARs run!\n");

    api_car_close(fd_car);
    
    return 0;
}
该应用程序是一个简单的测试例子,主要用来测试驱动程序的接口是否能成功的驱动小车的电机。当然你也可以用WiringPi或者Python GPIO去直接控制树莓派端口的电平来达到控制小车方向的目的,但是自己实现一个简单的GPIO端口驱动有利于我们对内核驱动的相关概念有更深入的理解。下节,我会讲一下如何在web端实现一个简单的小车遥控控制台。(当然用钱柜娱乐开户手机遥控也是很简单的),最后看看实物图:

1
0
查看评论
发表评论
* 以上用户言论只代表其个人钱柜娱乐开户,不代表CSDN网站的钱柜娱乐开户或立场

Google工程师:教你用树莓派+Arduino+TensorFlow搭建图像识别小车

雷锋网按:本文作者赵智沉,Google软件工程师。来自知乎专栏:赵智沉的作坊。雷锋网(公众号:雷锋网)获授权转载。 从买第一个Arduino套装开始,我接触机器人有好几年了,但直到最近才...
  • x32sky
  • x32sky
  • 2017-04-07 13:42
  • 1966

利用树莓派做智能小车

年初的时候看到@段念-段文韬 的这篇文章《使用树莓派制作的远程开门器》后,觉得硬件编程似乎没有想象的难。 之前认为硬件编程可能需要学习新的编程语言,需要特别的编程环境。然而树莓派使用Linux操作...
  • fanmengmeng1
  • fanmengmeng1
  • 2015-07-21 21:13
  • 5759

树莓派WiFi小车资料

  • 2016-08-07 10:36
  • 10KB
  • 下载

【raspberry pi】树莓派小车

一、组件介绍 大家好,我是Sunny。今天将给大家带来系列教程《树莓派小车系列》,先给大家介绍介绍我这次使用到的一些组件。 1、树莓派 2、小车底盘 3、小车车轮(马达) ...
  • g1fdgfgdf2_
  • g1fdgfgdf2_
  • 2017-11-24 11:53
  • 101

制作树莓派wifi遥控和自动避障小车

写在前面前几年买了树莓派,当时主要是想用来做客厅多媒体盒,不过实际使用下来有点卡,速度不尽人意。虽然后来换了class10的SD卡速度有点提升,但也很少拿去看电影了,多数时间是用来做下载机。其实之前也...
  • longlongago2000
  • longlongago2000
  • 2016-04-22 21:20
  • 8525

树莓派智能监控小车(QT+树莓派)------整体思路

本人大三学生,今年暑假将进入公司实习。目前学校做大学期间(除毕业设计外)的最后一个课程设计,要求是用到树莓派(嵌入式专业的)。因此,我和我的队友就打算做一个智能实时监控小车(网上有很多例子),打算实现...
  • hhf15980873586
  • hhf15980873586
  • 2017-06-24 22:47
  • 621

钱柜娱乐开户手机控制树莓派制作的四驱小车

年初的时候看到@段念-段文韬 的这篇文章《使用树莓派制作的远程开门器》后,觉得硬件编程似乎没有想象的难。 之前认为硬件编程可能需要学习新的编程语言,需要特别的编程环境。然而树莓派使用Linux操作...
  • fuzhouzhangwu
  • fuzhouzhangwu
  • 2015-03-09 20:13
  • 1035

安卓端APP遥控树莓派小车

在钱柜娱乐开户端写一个小小的控制器来通过局域网来控制树莓派小车的行驶,基本思路是在树莓派上写一个socket服务器,钱柜娱乐开户端写一个socket客户机,两边约定好命令的指令(例如可以简单的把停...
  • qq_22042587
  • qq_22042587
  • 2017-05-25 22:00
  • 8976

在安卓下控制基于树莓派的小车 皆用python实现

参照了lessisawesome的文章,/maninbehind/article/details/9715137 小车端(即树莓派),记得装RPI G...
  • baidang201
  • baidang201
  • 2014-03-30 14:57
  • 5559

树莓派小车物体追踪

  • 2017-12-04 23:18
  • 17KB
  • 下载
    个人资料
    • 访问:5940次
    • 积分:157
    • 等级:
    • 排名:千里之外
    • 原创:8篇
    • 转载:6篇
    • 译文:0篇
    • 评论:0条