Qualcomm 智能小车 驱动
经过不断的修修改改,硬件的准备工作终于彻底完成了,小车最基本的功能算是完成了,像前进、后退、左转、右转、调速、避障等。接下来就看一下相关驱动的实现。
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个红外避障模块的状态,从而作出相应的动作。
驱动部分就介绍这些,其他的比较简单就不再赘述了。
更多回帖