Qualcomm技术论坛
直播中

王林

7年用户 192经验值
私信 关注
[经验]

基于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: %dn",

            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 %dn",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 %dn",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 timen",__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 gpio1n");

        }

        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 gpio4n");

        }

        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 gpion",__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,"%#xn",data->distance);

    printk("%#xn",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 errorn",__FUNCTION__);

        return -ENOMEM;   

    }

    dev_set_drvdata(&pdev->dev,data);

    result = parse_dt(pdev,data);

    if(result<0){

        pr_err("%s error when parse dtn",__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 irq1n");

            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 sucessn");

    return 0;

err_parse_dt:

    kfree(data);

    printk("irda probe failedn");

    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个红外避障模块的状态,从而作出相应的动作。




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

更多回帖

发帖
×
20
完善资料,
赚取积分