基于Dragonboard 410c的智能小车(四)

      经过不断的修修改改,硬件的准备工作终于彻底完成了,小车最基本的功能算是完成了,像前进、后退、左转、右转、调速、避障等。接下来就看一下相关驱动的实现。

     1.首先是pwm控制驱动,在android 5.1.1的源码里面,是不会生成pwm那个节点的,所以这部分需要我们自己添加,这部分可以参考:https://lwn.net/Articles/553755/

      接下来就是pca9685的驱动了:


static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                  int duty_ns, int period_ns)
{
    struct pca9685 *pca = to_pca(chip);
    unsigned long long duty;
    unsigned int reg;
    int prescale;

    if (period_ns != pca->period_ns) {
        prescale = DIV_ROUND_CLOSEST(PCA9685_OSC_CLOCK_MHZ * period_ns,
                         PCA9685_COUNTER_RANGE * 1000) - 1;

        if (prescale >= PCA9685_PRESCALE_MIN &&
            prescale <= PCA9685_PRESCALE_MAX) {
            /* Put chip into sleep mode */
            regmap_update_bits(pca->regmap, PCA9685_MODE1,
                       MODE1_SLEEP, MODE1_SLEEP);

            /* Change the chip-wide output frequency */
            regmap_write(pca->regmap, PCA9685_PRESCALE, prescale);

            /* Wake the chip up */
            regmap_update_bits(pca->regmap, PCA9685_MODE1,
                       MODE1_SLEEP, 0x0);

            /* Wait 500us for the oscillator to be back up */
            udelay(500);

            pca->period_ns = period_ns;

            /*
             * If the duty cycle did not change, restart PWM with
             * the same duty cycle to period ratio and return.
             */
            if (duty_ns == pca->duty_ns) {
                regmap_update_bits(pca->regmap, PCA9685_MODE1,
                           MODE1_RESTART, 0x1);
                return 0;
            }
        } else {
            dev_err(chip->dev,
                "prescaler not set: period out of bounds!\n");
            return -EINVAL;
        }
    }

    pca->duty_ns = duty_ns;

    if (duty_ns < 1) {
        if (pwm->hwpwm >= PCA9685_MAXCHAN)
            reg = PCA9685_ALL_LED_OFF_H;
        else
            reg = LED_N_OFF_H(pwm->hwpwm);

        regmap_write(pca->regmap, reg, LED_FULL);

        return 0;
    }

    if (duty_ns == period_ns) {
        /* Clear both OFF registers */
        if (pwm->hwpwm >= PCA9685_MAXCHAN)
            reg = PCA9685_ALL_LED_OFF_L;
        else
            reg = LED_N_OFF_L(pwm->hwpwm);

        regmap_write(pca->regmap, reg, 0x0);

        if (pwm->hwpwm >= PCA9685_MAXCHAN)
            reg = PCA9685_ALL_LED_OFF_H;
        else
            reg = LED_N_OFF_H(pwm->hwpwm);

        regmap_write(pca->regmap, reg, 0x0);

        /* Set the full ON bit */
        if (pwm->hwpwm >= PCA9685_MAXCHAN)
            reg = PCA9685_ALL_LED_ON_H;
        else
            reg = LED_N_ON_H(pwm->hwpwm);

        regmap_write(pca->regmap, reg, LED_FULL);

        return 0;
    }

    duty = PCA9685_COUNTER_RANGE * (unsigned long long)duty_ns;
    duty = DIV_ROUND_UP_ULL(duty, period_ns);

    if (pwm->hwpwm >= PCA9685_MAXCHAN)
        reg = PCA9685_ALL_LED_OFF_L;
    else
        reg = LED_N_OFF_L(pwm->hwpwm);

    regmap_write(pca->regmap, reg, (int)duty & 0xff);

    if (pwm->hwpwm >= PCA9685_MAXCHAN)
        reg = PCA9685_ALL_LED_OFF_H;
    else
        reg = LED_N_OFF_H(pwm->hwpwm);

    regmap_write(pca->regmap, reg, ((int)duty >> 8) & 0xf);

    /* Clear the full ON bit, otherwise the set OFF time has no effect */
    if (pwm->hwpwm >= PCA9685_MAXCHAN)
        reg = PCA9685_ALL_LED_ON_H;
    else
        reg = LED_N_ON_H(pwm->hwpwm);

    regmap_write(pca->regmap, reg, 0);

    return 0;
}
....................

static const struct pwm_ops pca9685_pwm_ops = {
    .enable = pca9685_pwm_enable,
    .disable = pca9685_pwm_disable,
    .config = pca9685_pwm_config,
    .request = pca9685_pwm_request,
    .free = pca9685_pwm_free,
    .owner = THIS_MODULE,
};

static const struct regmap_config pca9685_regmap_i2c_config = {
    .reg_bits = 8,
    .val_bits = 8,
    .max_register = PCA9685_NUMREGS,
    .cache_type = REGCACHE_NONE,
};

static int pca9685_pwm_probe(struct i2c_client *client,
                const struct i2c_device_id *id)
{
    struct device_node *np = client->dev.of_node;
    struct pca9685 *pca;
    int ret,ret1;
    int mode2;
    printk("pca9685_pwm_probe start------------- \n");
    pca = devm_kzalloc(&client->dev, sizeof(*pca), GFP_KERNEL);
    if (!pca)
        return -ENOMEM;

    pca->regmap = devm_regmap_init_i2c(client, &pca9685_regmap_i2c_config);
    if (IS_ERR(pca->regmap)) {
        ret = PTR_ERR(pca->regmap);
        dev_err(&client->dev, "Failed to initialize register map: %d\n",
            ret);
 
        return ret;
    }
    pca->duty_ns = 0;
    pca->period_ns = PCA9685_DEFAULT_PERIOD;

    i2c_set_clientdata(client, pca);

    regmap_read(pca->regmap, PCA9685_MODE2, &mode2);
    printk("pca9685_pwm_probe mode2 is %d\n",mode2);
    if (of_property_read_bool(np, "invert"))
        mode2 |= MODE2_INVRT;
    else
        mode2 &= ~MODE2_INVRT;

    if (of_property_read_bool(np, "open-drain"))
        mode2 &= ~MODE2_OUTDRV;
    else
        mode2 |= MODE2_OUTDRV;

    ret1 = regmap_write(pca->regmap, PCA9685_MODE2, mode2);
    printk("pca9685_pwm_probe ret1 is %d\n",ret1);
    

    /* clear all "full off" bits */
    regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_L, 0);
    regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_H, 0);

    pca->chip.ops = &pca9685_pwm_ops;
    /* add an extra channel for ALL_LED */
    pca->chip.npwm = PCA9685_MAXCHAN + 1;

    pca->chip.dev = &client->dev;
    pca->chip.base = -1;
    pca->chip.can_sleep = true;
    printk("pca9685_pwm_probe end-------------\n");
    return pwmchip_add(&pca->chip);
}

static int pca9685_pwm_remove(struct i2c_client *client)
{
    struct pca9685 *pca = i2c_get_clientdata(client);

    regmap_update_bits(pca->regmap, PCA9685_MODE1, MODE1_SLEEP,
               MODE1_SLEEP);

    return pwmchip_remove(&pca->chip);
}

static const struct i2c_device_id pca9685_id[] = {
    { DRIVER_NAME, 0 },
    //{ "pca9685-pwm", 0 },
    { /* sentinel */ },
};
MODULE_DEVICE_TABLE(i2c, pca9685_id);

static const struct of_device_id pca9685_dt_ids[] = {
    { .compatible = "nxp,pca9685-pwm", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, pca9685_dt_ids);

static struct i2c_driver pca9685_i2c_driver = {
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
        .of_match_table = pca9685_dt_ids,
    },
    .probe = pca9685_pwm_probe,
    .remove = pca9685_pwm_remove,
    .id_table = pca9685_id,
};

这部分加进去之后就可以进行调速了。


 2.接下来是红外避障模块的驱动了


static irqreturn_t irda_interrupt_handler1(int irq, void *ptr){
    struct doorbell_data* data = (struct irda_data*)ptr;
    mutex_lock(&data->data_lock);
    data->distance = (data->distance | 0x01);
    mutex_unlock(&data->data_lock);
    data-> data_ready = true;
    wake_up_interruptible(&data->data_queue);
    return IRQ_HANDLED;
}
 
....................
 
static void cmd_work_func(struct work_struct* work){
    struct irda_data* data = container_of(work,struct irda_data,cmd_work.work);
 
    if(gpio_get_value(data->rx_gpio[0])){
        data->distance = (data->distance & 0x0e);
    }
 
 .....................
 
    data->data_ready = true;
    wake_up_interruptible(&data->data_queue);
    schedule_delayed_work(&data->cmd_work,msecs_to_jiffies(data->poll_time));
}
 
static int parse_dt(struct platform_device* pdev,struct irda_data* data){
    int rc;
    struct device_node* node = pdev->dev.of_node;
    rc = of_property_read_u32(node,"thunder,poll_time",&data->poll_time);
    if(rc){
        pr_warning("%s you should point time\n",__FUNCTION__);
        data->poll_time = 20;
    }
    data->rx_gpio[0] = of_get_named_gpio(node,"thunder,gpio_rx1",0);
    if(gpio_is_valid(data->rx_gpio[0])){
        rc = gpio_request(data->rx_gpio[0],"car_rx_gpio1");
        if(rc < 0){
            pr_err("uanble to request rx gpio1\n");
        }
        rc = gpio_direction_input(data->rx_gpio[0]);
    }
 
......................
 
    data->rx_gpio[3] = of_get_named_gpio(node,"thunder,gpio_rx4",0);
    if(gpio_is_valid(data->rx_gpio[3])){
        rc = gpio_request(data->rx_gpio[3],"car_rx_gpio4");
        if(rc < 0){
            pr_err("uanble to request rx gpio4\n");
        }
        rc = gpio_direction_input(data->rx_gpio[3]);
    }
 
    if(data->rx_gpio[0]<0 || data->rx_gpio[1]<0 || data->rx_gpio[2]<0 || data->rx_gpio[3]<0) {
        pr_err("%s,error gpio\n",__FUNCTION__);
        return -EINVAL;
    }
    return 0;
}
 
static ssize_t doorbell_show_value(struct device *dev,
                                  struct device_attribute* attr,char* buf){
    struct doorbell_data* data = dev_get_drvdata(dev);
    ssize_t lenth;
    wait_event_interruptible(data->data_queue,data->data_ready);
    data->data_ready = false;
    mutex_lock(&data->data_lock);
    lenth = sprintf(buf,"%#x\n",data->distance);
    printk("%#x\n",data->distance);        
    mutex_unlock(&data->data_lock);
    return lenth;
}
 
static DEVICE_ATTR(value,0644,irda_show_value,NULL);
 
static int irda_probe(struct platform_device *pdev){
    struct irda_data* data;
    int result;
    data = kmalloc(sizeof(struct irda_data),GFP_KERNEL);
    if(!data){
        pr_err("%s kmalloc error\n",__FUNCTION__);
        return -ENOMEM;   
    }
    dev_set_drvdata(&pdev->dev,data);
    result = parse_dt(pdev,data);
    if(result<0){
        pr_err("%s error when parse dt\n",__FUNCTION__);
        result = -EINVAL;
        goto err_parse_dt;
    }
    
        data->irq[0] = gpio_to_irq(data->rx_gpio[0]);
 
        result = request_irq(data->irq[0],doorbell_interrupt_handler1,IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,"irda_int1",data);
        if(result<0){
            pr_err("Unable to request irq1\n");
            goto err_parse_dt;
        }
 
.....................
 
    INIT_DELAYED_WORK(&data->cmd_work,cmd_work_func);
    mutex_init(&data->data_lock);
    init_waitqueue_head(&data->data_queue);
    
    schedule_delayed_work(&data->cmd_work,msecs_to_jiffies(data->poll_time));
    result=sysfs_create_file(&pdev->dev.kobj,&dev_attr_value.attr);
    printk("irda probe sucess\n");
    return 0;
err_parse_dt:
    kfree(data);
    printk("irda probe failed\n");
    return result;
}
static int irda_remove(struct platform_device *pdev){
    return 0;
}
 
static struct of_device_id doorbell_match_table[] = {
    { .compatible = "thund,irda",},
    { },
};
 
static struct platform_driver irda_driver = {
    .probe = irda_probe,
    .remove = irda_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "irda",
        .of_match_table = irda_match_table,
    },
};

    这部分驱动,主要是通过一个节点来监控4个红外避障模块的状态,从而作出相应的动作。


   驱动部分就介绍这些,其他的比较简单就不再赘述了。

你可能感兴趣的:(基于Dragonboard 410c的智能小车(四))