一.耳機的基本知識
1.手機全部使用了標准的3.5mm接口,對於一款采用3段式3.5mm接口的耳機來說是不存在兼容問題了,但采用四段式的線控耳機仍舊是有不兼容情況。常見的三段式接口叫做TRS端子,四段式為TRRS端子

2. 智能手機所使用四段式的3.5mm TRRS端子是有兩種接口定義標准的,OMTP和CTIA,OMTP就是我們俗稱的國家標准,CTIA是國際標准,兩種標准在接口前兩段的定義都是相同的。最新的手機基本都使用了CTIA標准。

3. 三段式的TRS端子的定義,從上到下依次是左聲道、右聲道和接地,這也就是為什么它能兼容任何手機的原因了。

4.線控結構,其實原理也很簡單,耳機接口上用於輸入信號的只有麥克風正極了,既然多鍵線控耳機一樣是使用4段式的接口,那么線控的命令一定也是通過這里輸入手機的。

市面上常見的線控類型大致有這幾種,音量滑塊帶麥線控、單鍵帶麥和多鍵帶麥,通過音量滑塊改變音量原理很簡單,只要在電路當中串入可變電阻即可實現。采用多按鍵的就比較復雜了,但依舊是通過改變電阻來實現的,從原理電路圖上不難看出,不同的按鍵下串入了不同的電阻,當按鍵被按下后不同的電阻被接入到了回路當中,麥克風正極接收到的信號也隨之發生改變,借此手機可以來識別用戶按下的是哪個按鍵,應該執行何種操作。
二.MTK平台耳機的檢測方式
1.Accdet only
此方式是讓耳機micbias 常開下,依靠耳機內部中斷來檢測耳機處於的狀態的。但此方式會帶來耳機插入瞬間有pop雜音的出現。
2.Accdet+EINT方式
此方式在耳機為插入時,micbias是被disable的。利用中斷 EINT來打開micbias`,從而達到省功耗和減小雜音的效果的。 待插入后,耳機檢測走的路線還是accdet內部中斷。
三.Accdet檢測pin內部比較器
電壓范圍說明
mt6589/72:
– Plug out state: 1.77V Voltage 1.9V (A=1, B=1);– Mic Bias state: 0.5V Voltage<1.77V (A=0, B=1);– Hook Switch state: 0V Voltage < 0.5V (A=0, B=0).
其中Hook switch multi-key電壓分布:
0v<=MD<0.09V<=UP<0.24V<=DW<0.5V
0v<=MD<0.09V<=UP<0.24V<=DW<0.5V
內部通過adc讀取電壓值,
MD:middle keyUP: up keyDW:down key
另外一種:看芯片手冊(pmu)或者驅動
MD UP DW
|--------------------|---------------|---------------|
0V<=MD< 0.08V<= UP<0.22V<=DW <0.5V
|--------------------|---------------|---------------|
0V<=MD< 0.08V<= UP<0.22V<=DW <0.5V
耳機按鍵的電阻值推薦如下:
Middle Key:0歐Up Key:220歐Down Key:600歐
ADC讀取的耳機按鍵電壓是根據基准電壓1.9V來輸出,就算MICBIAS電壓調整到2.7V,PMIC ADC芯片內部會根據
讀取到的電壓轉換基准電壓后輸出一個值。
這就解釋了為什么MICBIAS電壓調整后,耳機按鍵檢測的電壓范圍不改變的原因。
四. ACCDET模塊耳機檢測的原理
ACCDET內部有兩個比較器,會根據傳入的電壓判斷並產生中斷。ACCDET的輸入電壓即耳機 MIC PIN 的電壓,內部比較器的輸出分別對應A/B兩個寄存器。
ACCDET內部兩個比較器的Vref分別是1.77V和0.5V(硬件決定的,不能修改),所以,對應的電壓有 3個范圍:
1.77V-1.9V: 未插入耳機的狀態(AB=B11)
0.5V-1.77V: 插入4段式(有Mic)耳機時的狀態(AB = B01)
0-0.5V : 插入3段式耳機時的狀態, 或者4段式按鍵按下時的狀態(AB = B00)
耳機的狀態
會保存到ACCDET的寄存器中,當電壓在任意2個范圍間切換時,狀態發生變化,ACCDET產生 中斷,中斷處理中讀取狀態寄存器的值,並根據狀態的變化做相應的處理.
如果電壓調整到2.5或者其他.各段會按照比例變化。
五. 調整耳機MICBIAS電壓的方法
修改文件
alps\kernel-3.10\drivers\misc\mediatek\mach\$platform\$project\accdet\accdet_custom_def.h
修改下面的宏:
#define ACCDET_HIGH_VOL_MODE#ifdef ACCDET_HIGH_VOL_MODE#define ACCDET_MIC_VOL 7 //2.7v#else#define ACCDET_MIC_VOL 2 //1.9v#endif
其中數字表示的電壓如下:
MIC Bias 0 Output voltage selection
0: 1.7V1: 1.8V2: 1.9V3: 2.0V4: 2.1V5: 2.3V6: 2.5V7: 2.7V
六.驅動分析
驅動入口:
accdet_mod_init: Accdet_drv.c (kernel-3.10\drivers\misc\mediatek\accdet)
platform_driver_register(&accdet_driver); //調用accdet_probe函數
accdet_probe
mt_accdet_probe(); //調用的這個函數在Accdet.c (kernel-3.10\drivers\misc\mediatek\accdet\mt6735)
press_key_time = get_headset_key_custom_setting(); //得到長按的時間:在Accdet_custom.c (kernel-3.10\drivers\misc\mediatek\mach\mt6735\lentk6753_65c_l1\accdet)中
switch_dev_register(&accdet_data); //創建sysfs接口
cdev_add(accdet_cdev, accdet_devno, 1); //添加字符設備
init_timer(&micbias_timer); //定時器用於插入三段式耳機后關閉
micbias,因為不需要錄音
input_register_device(kpd_accdet_dev) //注冊input子系統用於耳機按鍵上報
INIT_WORK(&accdet_work, accdet_work_callback); //
accdet_work的工作隊列
pmic_register_interrupt_callback(12,accdet_int_handler); //設置中斷的調用函數
pmic_register_interrupt_callback(13,accdet_eint_int_handler);
accdet_init(); //初始化accdet,這個pmu中的寄存器
queue_work(accdet_workqueue, &accdet_work); //schedule a work for the first detection ,第一次檢測
INIT_WORK(&accdet_disable_work, disable_micbias_callback); //初始化micbias的工作隊列
INIT_WORK(&accdet_eint_work, accdet_eint_work_callback); //accdet中斷的工作隊列
INIT_WORK(&sendkey_eint_work, sendkey_eint_work_callback); //自定義的sendkey工作隊列
accdet_setup_eint //初始化中斷
mt65xx_eint_registration(, accdet_eint_func, 0);中斷處理函數為accdet_eint_func
到這里初始化完成了
執行第一次檢測:比如耳機插入后開機這種情況
queue_work(accdet_workqueue, &accdet_work);
accdet_work_callback
check_cable_type //檢測耳機的狀態,就是是不是一上電已經插入了
switch_set_state((struct switch_dev *)&accdet_data, cable_type); //如果已經插入就上報
kobject_uevent_env //填充上報環境變量
kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE); //上報,發送改變
當開機后插入耳機時,int中斷產生
accdet_eint_func
if(cur_eint_state == EINT_PIN_PLUG_IN ) //如果已經插入了,現在就是拔出
cur_eint_state = EINT_PIN_PLUG_IN; //沒有插入,就設置為插入狀態
queue_work(accdet_eint_workqueue, &accdet_eint_work); //調用accdet_eint_work工作隊列
accdet_eint_work_callback //調用相應函數
eint_accdet_sync_flag = 1; //設置為1
accdet_init();// do set pwm_idle on in accdet_init,初始化accdet
enable_accdet(ACCDET_SWCTRL_EN); //啟動accdet
//Turn on micbias always
pmic_pwrap_write(ACCDET_PWM_WIDTH, cust_headset_settings->pwm_width); //打開micbias ,公司定義的
pmic_pwrap_write(ACCDET_PWM_THRESH, cust_headset_settings->pwm_width);//公司定義的
sendkey_setup_eint(); //sendkey中斷注冊,公司定義的
檢測按鍵
當按下按鍵accdet檢測到電壓區域變化: 通過pmu的中斷,pm會調用設置callback函數,分別是pmu的中斷12和中斷13
pmic_register_interrupt_callback(12,accdet_int_handler); //設置中斷的調用函數pmic_register_interrupt_callback(13,accdet_eint_int_handler);
accdet_int_handler和 accdet_eint_int_handler都調用accdet_irq_handler
accdet_irq_handler
accdet_get_current_time //得到現在的時間
clear_accdet_interrupt(); //清中斷
accdet_workqueue_func
queue_work(accdet_workqueue, &accdet_work); //執行函數為accdet_work_callback
accdet_work_callback
check_cable_type(); //
current_status = ((pmic_pwrap_read(ACCDET_STATE_RG) & 0xc0)>>6) //得到現在的狀態, 包括按鍵檢測,三段式耳機檢測等
if(1 == eint_accdet_sync_flag) { switch_set_state((struct switch_dev *)&accdet_data, cable_type); /、
七.在kernel層實現sendkey的長按,雙擊,單擊上報:accdet.c
附件中用
#ifdef RUNBO_CUST_HEADSET
#endif
包含的代碼是用於實現這個功能
注意:mic的mode很重要,有沒有接電容可能導致mic無聲,還有五六腳一定要接地。mic的P端的電壓一般在0.7,N端接地。