锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

msm8953平台 sensor笔记

时间:2023-12-08 07:37:01 sensor传感器dl100

typedef struct
{
qmi_client_os_params os_params;
qmi_client_type user_handle;
qmi_client_type notifier_handle;
uint8_t svc_num;
} smgr_mr_qmi_client_if_s;


typedef struct
{
/* QMI client interface */
smgr_mr_qmi_client_if_s qmi_cl_reg;

sns_q_s msg_queue;
OS_EVENT* msg_queue_mutex_ptr;

sns_q_s req_queue;
OS_EVENT* req_queue_mutex_ptr;

} sns_smgr_mr_s;

sns_smgr_mr_msg_s* sns_smgr_mr_get_msg(void)
{
uint8_t os_err;
sns_smgr_mr_msg_s* msg_ptr = NULL;
/* ---------------------------------------------------------------------- */

sns_os_mutex_pend(sns_smgr_mr.msg_queue_mutex_ptr, 0, &os_err );

msg_ptr = (sns_smgr_mr_msg_s*) sns_q_get(&sns_smgr_mr.msg_queue);

(void)sns_os_mutex_post(sns_smgr_mr.msg_queue_mutex_ptr );

return msg_ptr;
}

sensor函数调用流程:
1.main() //sns_pd.c文件
2.coremain_main() /* Core Init for user PD */
3.qurt_elite_globalstate_init(); /* Init global state structure */////初始化值为0或0NULL
4.sns_init(); //sensor init ----->下一步是进入一个死循环,函数主要是 init 函数中;
5.sns_profiling_init(); /* 初始化模组 */ //m模组就是sensor芯片集成在一起PCB板上
6.sns_init_once(); /* const sns_init_fcn init_ptrs[] = SNS_INIT_FUNCTIONS; ///将函数名放入数组中 */
/* SNS_INIT_FUNCTIONS = {} 一系列的sensor初始化函数,根据宏定义来的,文件在sns_init.h中 */
7.sns_heap_init(); /* sensor 堆的初始化,堆的空间大小为 512K */
8.sns_os_sigs_create(); /* 核心是调用malloc()函数申请一个 os_FLAG_GNP 结构类型的变量 */
9.for循环依次执行 init_ptrs[i](); /* 一些初始化函数存储在函数名数组中,如:sns_init_dsps, sns_em _init, sns_smgr_init 等函数*/
10.sns_smgr_init();--> sns_os_task_create_ext(sns_smgr_task, NULL,...);//smgr: sensors manager 通过这个函数进入sensors驱动函数;
/*
#ifdef FEATURE_MSM8960
# define SNS_INIT_FUNCTIONS \
{ sns_memmgr_init, \ // 内存管理器
sns_init_dsps, \ // 各种dsps服务的初始化
sns_em_init, \ // 事件管理器
sns_smr_init, \ // Message Router用于传递信息(resp/ind)
sns_dl_init, \ // Dynamic Loading service
sns_smgr_init, \ // Sensor Manager(核心部分)
sns_scm_init, \ // 检验管理器
sns_sam_init, \ // 算法管理器
sns_pm_test_task_init, \ //
dog_init, \
NULL }
*/
11.调用sns_os_task_create_ext()函数中的sns_smgr_task函数初始化SMGR 模块;
12.sns_init();/* sensor 初始化 */
13.sns_init_once(); /* */
14.sns_smgr_init(); /* */
15.sns_smgr_task(); /* */
16.sns_smgr_hw_init(); /* 进行一些的ADSP初始化和sensors模块硬件初始化;并调用sns_smgr_process_msg(void) */
17.sns_smgr_process_msg(void); /* 从消息队列中获取要处理的消息,并在此函数中向下传输参数变量 ,函数遍历调用*/
SMGR_STATIC void sns_smgr_process_msg(void)
{
sns_smgr_mr_msg_s* msg_ptr;

sns_smgr_update_last_tick();

whle ( NULL != (msg_ptr = sns_smgr_mr_get_msg()) )
   {
     sns_smgr_mr_header_s* hdr_ptr = &msg_ptr->header;
     SNS_SMGR_PRINTF3(
       MED, "process_msg - svc=%d msg=0x%x now=%u",
       hdr_ptr->svc_num, hdr_ptr->msg_id, sns_smgr.last_tick);
 
     if ( SNS_REG2_SVC_ID_V01 == hdr_ptr->svc_num )
     {
       sns_smgr_process_reg_resp_msg(msg_ptr);
     }
     else
     {
       SNS_SMGR_PRINTF0(ERROR, "process_msg - unexpected message");
     }
   }
 }

18.经过一系列的函数调用,最后去调用驱动文件里面的函数,如:.init, get_data, reset等函数;把获取的数据放到结构体中,在传回上层;
19.例如下图:
  typedef struct
  {    
     sns_ddf_handle_t     i2c_handle; ---->typedef void * sns_ddf_handle_t;
     sns_ddf_handle_t     smgr_handle;
 
     uint8_t              dev_id;  
     adxl350_range_e      range;
     adxl350_data_rate_e  data_rate;    ---->设备传输速率;
     adxl350_power_mode_e power_mode;   ---->设备的电源状态
     adxl350_sleep_freq_e sleep_freq;   
     adxl350_fifo_mode_e  fifo_mode;
     adxl350_resolution_e resolution;
 
     uint8_t              intr_enable[ADXL350_INTR_NUM];
     int8_t               offset[ADXL350_NUM_AXES];
     int16_t              data_raw[ADXL350_NUM_AXES];
     q16_t                data_cache[ADXL350_NUM_AXES];
     uint32_t             retry_delay_usec;
 #ifdef ADXL350_DEBUG
     uint32_t samples_counter;
     uint32_t retry_counter;
 #endif
 } adxl350_drv_t;

 /*在vendor/qcom/non-hlos-msm8953/ADSP.8953.2.8.4/adsp_proc/Sensors/dd/qcom/src目录下,属于BP的东西*/
 sns_ddf_driver_if_s SNS_DD_IF_BMI160 =                                                                                                              
 {
     /* 5555550X */
     .init                 = &sns_dd_bmi160_init,
     /* 5555551X */
     .get_data             = &sns_dd_bmi160_get_data,
     /* 5555552X */
     .set_attrib           = &sns_dd_bmi160_set_attr,
     /* 5555553X */
     .get_attrib           = &sns_dd_bmi160_get_attr,
     /* 5555554X */
     .handle_timer         = &sns_dd_bmi160_handle_timer,
     /* 5555555X */
     .handle_irq           = &sns_dd_bmi160_interrupt_handler,
     /* 5555556X */
     .reset                = &sns_dd_bmi160_reset,
     /* 5555557X */
     .run_test             = &sns_dd_bmi160_self_test,
     /* 5555558X */
     .enable_sched_data    = &sns_dd_bmi160_enable_sched_data,
     /* 5555559X */
     .probe                = &sns_dd_bmi160_probe,
     /* 5555560X */
 #if BMI160_CONFIG_ENABLE_FIFO
     .trigger_fifo_data    = &sns_dd_bmi160_trigger_fifo_data
 #endif
 };

每一个芯片驱动,都会有一系列的函数,如:初始化设备函数,获取数据,设置/获取属性,中断处理,复位等函数;
最后把这一系列函数放在一个数字列表中,让上层去通过这个结构体变量去来调用函数指针,
结构体的类型都是 sns_ddf_driver_if_s 被其他的函数拿过来进行回调;

sns_ddf_driver_if_s 结构体成员变量:
                    1.init();
                    2.get_data();--->函数变量成员sns_ddf_sensor_e  sensors[];里面存放一些设备的寄存器地址;
                    3.set_attrib();  ---->设置属性
                    4.get_attrib();  ---->获取属性
                    5.handle_timer(); 定时器函数,初始化定时器,通过定时器来执行get_data()函数;
                    6.handle_irq();  中断处理函数,sensor芯片引脚中有一个中断引脚;
                    7.run_test();   工厂测试模式;
                    8.enable_sched_data();   --->获取sensor数据,第二种方式;通过DRI方式;
                    9.probe();   匹配函数,根据给定的配置来匹配,匹配成功调用probe()函数;
                    10.trigger_fifo_data();   获取一组数据,通过FIFO的方式;
                    11.process_daf_req();  将driver framework 消息传递给驱动;---一系列的请求
                    12.cancel_daf_trans();  取消当前消息传输框架


20.驱动调用read()函数,最后调用到QUP这个模块,read()函数中去筛选是I2C/SPI总线去访问;
21.sns_ddf_comm_bus_i2c_write( handle, reg_addr, buffer, bytes, &bytes_written );//向总线去访问数据
22.最后调用adsp_proc/core/buses/i2c/src/drv/I2cDriver.c文件中的驱动函数;

Sensor上报数据的方式分为如下几种

sync          同步数据上报,(每次上报一个数据)

async        异步数据上报,每次请求之后不阻塞,定时查看状态,(收到一个数据即上报)

self-scheduling         异步数据上报,每次请求之后不阻塞,等待中断或定时查看状态,(收到一个数据即上报)

FIFO          异步数据上报,每次请求一组数据,当传感器数据累积设定水位,由水位中断触发一组数据上报。

S4S(Synchronization for Sensors)              用来同步时钟,避免数据遗漏或同一数据被取两次
                    
 sensor数据上报方式:
    1.Polling(0x00):1.(异步方式)调用一次get_data后启动timer,等到timer到时间后调用sns_ddf_driver_if_s中指定的handle_timer()函数上报一组传感器数据;()
                    2.(同步方式)smgr向传感器请求数据,阻塞等待数据到来再返回;
    2.DRI(0x80):调用enable_sched_data()启用DRI(Data ReadyInterrupt,数据完成中断),
    按照set_cycle_time指定的ODR(Output Data Rate,数据输出速率)进行数据采集,采集完成后调用sns_ddf_driver_if_s中指定的handle_irq()函数上报传感器数据。
    3.(FIFO)0xD0:调用trigger_fifo_data()函数启动FIFO模式,当数据量到达指定的阈值,触发sns_ddf_smgr_data_notify()函数上报一批数据

总结:每一款芯片挂载到I2C/SPI总线上的时候,去对设备进行读写的时候,要根据其芯片手册来进行相应的配置,才能正常的读写数据;故每一款的芯片设备结构体类型不一样;
     同时,这边并没有真正去调用kernel中的I2C/SPI驱动,因为ADSP是在BP的处理器上进行处理的,它去获取数据也是像 MCU 的那种方式去配置寄存器,最后通过QMI(共享内存的方式)
     把BP获取的数据传输给AP,
     

Framework--->JNI(Binder)(hw_get_module接口)--->HAL---->Sensor1--->QMI----->共享内存--->QMI--->SMGR--->Device driver Framework--->QUP(操作寄存器);

HAL层调用:processReportInd();来把从sensor1中获得数据给Framework层;
获取到的sensor数据会存放在sensors_event_t* data结构体中;

static int sensors_poll(struct sensors_poll_device_t *dev,sensors_event_t* data, int count)
 {
      SensorsContext *ctx = (SensorsContext *)dev;
      int ret = 0;
      //ATRACE_BEGIN("SSCHAL:sensors_poll"); 
      ret = ctx->poll(data, count);      
      //ATRACE_END();
      return ret;
 }
      2.上层是如何把接受数据的请求发送下去,BP端如何把数据传递上去的;
      3.上层会把请求数据消息放到一个消息队列里,让SMSR去读取队列里的消息去处理;------>数据消息是如何放到消息队列里的?
      
struct hw_module_t;              //模块类型  
struct hw_module_methods_t;      //模块方法  
struct hw_device_t;              //设备类型,向上提供接口  

sensorContext对象是通过HAL层中的代码中传出来的;
HAL模块通过sensorContext 对象来调用sensor1(sensor1_open()与SMGR建立连接)来通过一个线程libsensor_rx_thread()来获取底层的数据;(sensor1_init_once()函数中创建的线程)
然后把获取到的数据由sensor1_open()注册的client端传给sensor HAL;

这里可以看到,相当于创建了一个读线程,通过poll方法不停的在读消息,读到消息后,会将消息封装,然后唤醒另外一个线程,这个线程是谁我们后面会揭晓答案

总结sensor1_open就是创建一个读线程从socket服务端中读数据,读到数据后,就回调sensor.platform.so库中的注册的cb,进而上报数据做进一步回调


================================================================================================================================================
Native层这部分常见一些本地服务和一些链接库等。这一层的一个特点就是通过C和C++语言实现
native层如何调用到 HAL 层驱动?
1.上层的SensorService服务启动后,会去获取SensorDevice实例调用hw_get_module()函数来加载HAL模块;
2.hw_get_module()中通过load()函数加载sensor_xx.so文件;根据.so文件中的模块ID地址跳转到HAL模块,我们HAL层模块中的ID必须要其相同才能被调用;
3.跳转到HAL模块 struct sensors_module_t 中,该结构体变量中.common 传给 sensor_open1();来执行open函数;通过这个来向上层提供结构;

HAL层模块调用:
1.sensors_open()--->SensorsContext::SensorsContext()--->sensor1_init()

2.sensor1_init()--->sensor1_init_once();创建一个线程获取sensor数据(线程inotify_thread());-->使用select()函数来获取sensor数据,socket通信;
----->sns_os_task_once( &thread, sensor1_init_once );

3.sensor1_error_e sensor1_open( sensor1_handle_s **hndl,sensor1_notify_data_cb_t data_cbf,intptr_t cb_data );
API说明:注册一个外部client端socket到sensor framework,它会返回一个client端的handle句柄,这个句柄在后面会用于与sensor framework通信。
另外,他还注册了一个callback,当有可用的数据达到时,会携带数据并回调该接口。
receive的数据有两种,一种是通过request请求得到的回应reponse,一种是indication,即底层主动上报的数据。

现在新的高通架构采用的是建立socket通信机制;
msm8953平台采用的使用创建一个wait_thread()线程,并在线程中起了一个service,service来与QMI建立关系;(注:该线程主要任务是与qmi建立socket通信机制)
client->data_cbf = data_cbf;
client->cb_data = cb_data;
client->messages = NULL;
client->client_connections = NULL;
client->client_mutex = NULL;
client->messages_sem = NULL;(消息信号量)
然后sensor1这边有创建一个client_thread()线程来不断的获取消息数据;(信号量去实现消息的同步机制);--->通过inotify_thread()线程读取到数据来唤醒该线程;去上报数据;

4.sendSMGRVersionReq();-->向SMGR(sensor1_write)发送请求;--->Utility::waitForResponse();等待请求消息回复;-->这里会回调sensor1-open()里面的回调函数;
填充msg_hdr,包括service number,msg_id(SNS_SMGR_VERSION_REQ_V01),txn_id等,再通过sensor1_alloc_msg_buf向Sensor framework层分配smgr_version_req的请求buffer,
最后通过sensor1_write方法将请求的消息发送到sensor framework,这条消息最终会通过高通独有的QMI的机制,将消息从AP测发送到modem端处理,
modem端处理完毕后会回传请求的数据在通过注册的回调来接收所请求的数据

5.getSensorList();-->和上面发送消息的方式一样,会得到sensor info数据;
每个具体的sensor info返回时,都会调用processSingleSensorInfoResp进一步处理;
=====================================================================================================================================================================
上层起个service来获取HAL的数据;
SensorService::threadLoop()-->调用poll函数来获取sensor数据;最终调用SensorsContext::poll(sensors_event_t* data, int count);来从队列里面读取数据;
在stepCounter.cpp中,其processInd方法将驱动层上报的数据放入了队列里:(轮询)

注;每一个sensor的processInd都会有具体的实现,每个sensor的数据都有自己独有的数据结构,最终都会封装转换成sensors_event_t结构体插入到queue队列中去。

sensor的数据获取是通过一个线程client与server服务端获取的;
把获取的sensor数据放到队列中的函数调用:processInd()-->processBufferingInd()-->processReportInd();(文件在:dsps/libhalsensors/src/SMGRSensor.cpp)
在通过poll函数从队列里面去获取数据;

总结:也就是上层起了2个service,一个service来加载HAL驱动,另一个service来读取HAL驱动获取到的sensor数据;


问题:1. .c文件的获取数据和poll里面获取怎么取舍;
      2. 其他文件怎么加载到sensor_hal.cpp模块中的;
      
      
=======================================================================================================================================================================
sensorDaemon服务端的建立过程:
1.首先在系统init阶段的时候就加载了sensorDaemon初始化,然后在vendor下进行对sensorDaemon守护进程来建立通信;
2.sns_main_daemonize();创建了一个守护进程;-->该守护进程只是做了一个freopen()工作;
3.sns_init();-->负责Sensor Message Router(SMR)通信以传递客户端请求到SensorManager(SMGR),并回传response给到对应的客户端,
4.sns_main_setup();--->初始化服务端;

SensorDaemon的open过程注册的notify_cb是上面的流程处理的,即这是modem端抛给SensorDaemon的数据,SensorDaemon在将此数据通过socekt传递到hal层,
hal层再根据各个sensor自身数据结构将数据封装成sensor_event_t结构体,插入到Queue中,SensorService通过poll方法从这个Queue中读取数据,将此数据传递给对应的app。

QMI机制是AP侧和BP侧两端通信的一种机制;


sensor驱动的加载和添加;
    0.增加每一个驱动文件都需要把每一种sensor变量添加到头文件中,去包含这个sensor,表示我们的手机支持这一款sensor;路径:vendor/qcom/non-hlos-msm8953/ADSP.8953.2.8.4/adsp_proc/Sensors/dd/qcom/inc/
    1.添加新驱动的编译配置选项;  adsp_proc/sensors/build/Sensors.scons
    2.添加编译驱动的文件;(内核驱动文件一样) adsp_proc/sensors/dd/qcom/build/dd_qcom.scons
    3.生成 UUID 并与驱动建立联系;    每一个sensor驱动芯片都有它自己对应的UUID,通过UUID来匹配驱动; adsp_proc/Sensors/common/inc/sns_reg_common.h
    4.更新 Sensors Manager (SMGR) sensor函数指针和uuid; //调用对应的驱动文件,通过函数指针与对应的UUID号来配置,(smgr来把数据上报上去)adsp_proc/Sensors/smgr/src/sns_smgr_reg.c
    5.添加sensor对应的驱动文件; adsp_proc/sensors/dd/qcom/src;
    6.配置sensor相关配置文件 ,及push生效生成reg文件;
    6.1  Sensors/api/sns_reg_api_v02.h 文件中的宏值是表示对应的sensor的数值,也是上层在获取sensor数据的时候,通过这个值来获取,发送指令给底层,还有一部分是每一组senor的ID与sensor_def_qcomdev.conf文件里向对应;
    6.2  vendor/qcom/proprietary /sensors/dsps/reg_defaults/sensor_def_qcomdev.conf,该config文件里面存放着sensor的信息和一些传感器的默认值;
    该config里面的值格式:ID 对应的值  版本号;(下面的版本号和定义的版本号不一样)
    6.3 根据config文件版本号决定是否更新config文件;   注:若config配置有变化版本号未更新就会出现配置不生效的情况
        nv分区中,存放一些寄存器组的ID号
    7. .conf文件解析; vendor/qcom/proprietary/sensors/dsps/sensordaemon/reg/src/sns_reg_conf_la.c;
    可以使用QXDM软件去抓取init log和 发送一些测试命令
    
单独更新adsp bin方法如下:
adb root
adb wait-for-device
adb remount
adb shell mount -o rw,remount /firmware
adb shell rm /firmware/image/adsp* 
adb push adsp* /firmware/image/         (adsp.b00 ~ adsp.b14 、adsp.mdt)
adb shell rm /persist/sensors/sns.reg
adb shell sync
adb reboot

4.2 push配置使其生效
adb root  
adb remount  
adb shell rm /system/etc/sensors/sensor_def_qcomdev.conf  
adb push sensor_def_qcomdev.conf /system/etc/sensors/sensor_def_qcomdev.conf  
adb shell chmod 644 /system/etc/sensors/sensor_def_qcomdev.conf  
adb shell rm /persist/sensor/sns.reg  
adb shell sync  
adb reboot  

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章