AKM8975源代码分析:

源文件位置:kerne\lc1810\drivers\misc\akm8975.c

源代码分析:

采用模块化方法进行注册,通过module_init(akm8975_init)调用akm8975_init(void)通过i2c_add_driver(&akm8975_driver)添加akm8975的驱动到系统内核中。

akm8975_driver结构体包含以下几个函数,也就是对i2c_driver定义结构体的填充。

static struct i2c_driver akm8975_driver = {

        .probe =akm8975_probe,      系统加电后初始化配置函数

        .remove =akm8975_remove,   卸载函数

#ifndef CONFIG_HAS_EARLYSUSPEND 电源管理

        }

 akm8975_probe函数是加电后自动执行的第一个函数,该函数实现了设备启动配电后模块注册到系统内核的过程,对相关的寄存器及要使用的工作队列进行初始化。

1、首先通过client->dev.platform_data判断用户定义的私有数据是否为空。这个platform_data是在"设备"驱动取得的。

2、通过i2c_check_functionality(client->adapter, I2C_FUNC_I2C)验证i2c适配器功能是否正常。

3、通过函数kzalloc(sizeof(struct akm8975_data), GFP_KERNEL);akm8975_data module data分配内存空间及清零。

4、通过akm->pdata = client->dev.platform_data;把设备的数据赋给新分配的空间。

5INIT_WORK(&akm->work,akm_work_func);akm结构体中创建一个work的结构体变量,往这个结构体变量中添加处理函数入口地址akm_work_func

static voidakm_work_func(struct work_struct *work)

{ enable_irq(akm->this_client->irq);}开中断检测中断事件准备处理中断。

6err = akm8975_power_on(akm);加电

7akm8975_init_client(client);初始化设备。首先通过i2c得到设备数据地址,请求中断。通过函数init_waitqueue_head初始化队列头,添加中断的事件到队列中。互斥设置所有标志位为1.设备初始化完成。

8、声明input设备。Input_dev封装在akm8975_data变量当中,调用akm->input_dev = input_allocate_device();对其需要分配空间,成功后调用set_bit(EV_ABS,akm->input->dev)对其声明上报数据类型.

调用input_set_abs_params()函数设置上报数据的范围。完成后利用函数input_register_device(akm->input_dev);注册输入设备。

9err = misc_register(&akmd_device);

err = misc_register(&akm_aot_device);注册两个misc设备akmd_deviceakm_ato_device.

两个设备的定义如下:

static struct miscdeviceakm_aot_device = {

    .minor = MISC_DYNAMIC_MINOR,

    .name = "akm8975_aot",

    .fops = &akm_aot_fops,

};

static struct miscdevice akmd_device = {

    .minor = MISC_DYNAMIC_MINOR,

    .name = "akm8975_dev",

    .fops = &akmd_fops,

};这里注册了两个misc设备,具体作用还没有搞明白,一个通过akm_aot_ioctl,主要是获取和设置sensor状态(打开和关闭)和延时另外一个通过akmd_ioctl实现数据与内核层的写入和读取。

阅读akm_aot_ioctl代码,ECS_IOCTL_APP_SET_MVFLAG,表示设置原始的磁场矢量标志符。调用copy_from_user将参数argp拷贝到flag.设置完成后,在下一个开关语句中,直接赋值实现设置标识位为相应的状态,例如:

caseECS_IOCTL_APP_SET_MVFLAG:

             mv_flag = flag;

             break;

阅读akmd_ioctl()函数。首先分析ECS_IOCTL_READ,分别在以下三个开关函数中实现read功能。首先,判断参数有效后,调用copy_from_user将参数argp拷贝到rwbuf。然后,判断rwbuf区域没有越界后,调用ret = akm8975_i2c_rxdata(akm, &rwbuf[1], rwbuf[0])读数据。最后,调用copy_to_userrwbuf拷贝到argp参数。 ECS_IOCTL_READECS_IOCTL_WRITE主要是数据的传输过程,extenel层中获取数据的方式包括从akm8975的寄存器或者是eprom上,在AK8975Driver.c文件中调用。

akmd_ioctl()函数中,case ECS_IOCTL_SET_YPR:调用copy_from_user(&value, argp, sizeof(value))将参数argp拷贝到value中。在第二个开关中调用akm8975_ecs_report_value(akm, value);

上报点给上层。/* Report magnetic sensor information */

      if (m_flag) {

             input_report_abs(data->input_dev, ABS_RX, rbuf[0]);

             input_report_abs(data->input_dev, ABS_RY, rbuf[1]);

             input_report_abs(data->input_dev, ABS_RZ, rbuf[2]);

             input_report_abs(data->input_dev, ABS_RUDDER, rbuf[4]);

      }

 

case ECS_IOCTL_GET_OPEN_STATUS:

              wait_event_interruptible(open_wq,

                                    (atomic_read(&open_flag) != 0));

              status = atomic_read(&open_flag);

break;

源代码定义了等待队列,staticDECLARE_WAIT_QUEUE_HEAD(open_wq);cmdECS_IOCTL_GET_OPEN_STATUS时,读取状态,等待中断发生,有中断时把事件写入open_wq,放到队列中等待处理。

 

另外经过网上查资料,ECS_IOCTL_GET_TEST, ECS_IOCTL_SET_TEST, ECS_IOCTL_GET_TEST_RESULT, ECS_IOCTL_SET_TEST_RESULT,控制命令,这些命令对应的应该主要是原厂提供的算法库。

10err = device_create_file(&client->dev, &dev_attr_akm_ms1);使用这个函数时要引用 device_create所返回的device*指针,作用是在/sys/class/下创建一个属性文件,从而通过对这个属性文件进行读写就能完成对应的数据操作。http://www.hovercool.com/en/Special:Print?topic=Class_create%2C_device_create%2C_device_create_file#a_.E4.B8.89.E3.80.81device_create_file

在代码中定义了一个static DEVICE_ATTR(akm_ms1, S_IWUSR | S_IRUGO, akm8975_show, akm8975_store); 定义了一个akm8975_show读方法和写方法km8975_store。然后利用device_create_file创建一个属性文件,通过属性文件进行读写完成对设备的属性操作。

static ssize_t akm8975_show(struct device *dev, struct device_attribute *attr,
     char *buf)
{
 struct i2c_client *client = to_i2c_client(dev);
 return sprintf(buf, "%u\n", i2c_smbus_read_byte_data(client,
            AK8975_REG_CNTL));//Operation mode setting
}
static ssize_t akm8975_store(struct device *dev, struct device_attribute *attr,
       const char *buf, size_t count)
{
 struct i2c_client *client = to_i2c_client(dev);
 unsigned long val;
 strict_strtoul(buf, 10, &val);
 if (val > 0xff)
  return -EINVAL;
 i2c_smbus_write_byte_data(client, AK8975_REG_CNTL, val);//write Operation mode
 return count;
}

11、电源函数初始化。

akm->early_suspend.suspend = akm8975_early_suspend;

   akm->early_suspend.resume = akm8975_early_resume;

   register_early_suspend(&akm->early_suspend);

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
匿名科创四轴同款传感器方案设计ICM20602+AK8975+SPL06-001 电赛必备!!! 四轴必备!!! 四轴起飞时,发出触发信号使导航模块开始工作,同时读取ICM20602的加速度计、陀螺仪数据,对数据卡尔曼滤波后姿态解算,对角度与角速度采取串级PID调节。 控制系统算法设计主要有ICM20602滤波算法,姿态解算算法、串级PID控制算法和定高部分控制算法。碍于篇幅所限,下面介绍最重要的串级PID控制算法和定高部分控制算法。 地理坐标系中重力的水平分量为零,仅用三轴陀螺仪和三轴加速度计无法计算出航向角,由于巡线机器人保持稳定飞行只需要横滚角(roll)和俯仰角(pitch),所以四元数转换成欧拉角。 定高控制算法采用的是增量式PID控制,定高控制的输出最后与姿态控制的输出叠加到四个电机的控制中。数据滤波使用的是低通滤波,采用近三次的平均值。为了防止姿态对激光测距的影响及减小高度控制对姿态控制的干扰使用欧拉角来校正高度值,即Hight=(float)Hight*(cos(roll)* cos(pitch))。将四元数转换后的欧拉角与陀螺仪测出来的角速度进行串级PID控制,其中欧拉角作为外环,角速度作为内环。外环的PID以及内环的PD设定值为测试数据值。由于内环的角速度控制不需要无静差,所以内环采用PD控制,为防止测量的误差造成较大影响,外环积分需要限幅。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值