针对高通BMS的研究 高通电量计

摘要:
单击以打开链接。高通806489748926和其他pm芯片与功率表集成。据估计,后续芯片将一直存在。现在,许多项目的UI状态栏中显示了电池的百分比,因此有必要深入分析BMS以帮助解决电源问题。静态结构单行_路_路_温度={.x={-20,0,25,40,60},.y={31933190319031803183},.cols=5};2: RC:检查表格并计算启动开始时获得的开路电压百分比,以进行比较和计算;内核计算方法:[html]viewplatecoyprint?

点击打开链接

高通8064 8974 8926等pm芯片都集成了电量计,估计后续芯片都会一直存在,现在许多项目UI状态栏电池都有百分比显示,所以需要深入分析BMS有助于解决电量方面的BUG。


一: SOC(荷电状态)计算方法

名词:

FCC  Full-charge capacity      

UC     Remaining capacity
CC     Coulumb counter    
UUC  Unusable capacity
RUC   Remaining usable capacity //    RUC=RC-CC-UUC
SoC   State of charge    
OCV    Open circuit voltage

 SOC=(RC-CC-UUC)/(FCC-UUC)


以下是各个变量的计算方法:
1:FCC:
   在校准的电池profile中有定义,会随温度有变化;
  1. static struct single_row_lut fcc_temp = {  
  2.  .x  = {-20, 0, 25, 40, 60},  
  3.  .y  = {3193, 3190, 3190, 3180, 3183},  
  4.  .cols = 5  
  5. };  

2:RC: 开机通过开始获取的开路电压(ocv)来查表(电池校准的profile文件)计算百分比,来比对计算(电压与电荷量正比);(ocv=vbatt+rbatt*i_ma)
内核计算方法:
  1. static int calculate_remaining_charge_uah(struct pm8921_bms_chip *chip,  
  2.       struct pm8921_soc_params *raw,  
  3.       int fcc_uah, int batt_temp,  
  4.       int chargecycles)  
  5. {  
  6.  int  ocv, pc, batt_temp_decidegc;  
  7.   
  8.   
  9.  ocv = raw->last_good_ocv_uv;  
  10.  batt_temp_decidegc = chip->last_ocv_temp_decidegc;  
  11.  pc = calculate_pc(chip, ocv, batt_temp_decidegc, chargecycles);  
  12.  pr_info("ocv = %d pc = %d ", ocv, pc);  
  13.  return (fcc_uah * pc) / 100;  
  14. }  

但是通常情况下开机使用计算RC的ocv是上次关机存下的百分比,反向查表算出的ocv;
现在我们做法是通过判断开机时的ocv与关机的ocv如果偏差太大,我们将采用开机ocv来计算RC,所以开机的ocv对开机的百分比影响非常大;


3:CC:pmic库伦计 ADC采样到的:
内核获取方法:
  1. /**  
  2. * calculate_cc_uah -  
  3. * @chip:  the bms chip pointer  
  4. * @cc:   the cc reading from bms h/w  
  5. * @val:  return value  
  6. * @coulumb_counter: adjusted coulumb counter for 100%  
  7. *  
  8. * RETURNS: in val pointer coulumb counter based charger in uAh  
  9. *        (micro Amp hour)  
  10. */  
  11. static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val)  
  12. {  
  13.  int64_t cc_voltage_uv, cc_pvh, cc_uah;  
  14.   
  15.   
  16.  cc_voltage_uv = cc;  
  17.  pr_debug("cc = %d ", cc);  
  18.  cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);  
  19.  cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);  
  20.  pr_debug("cc_voltage_uv = %lld microvolts ", cc_voltage_uv);  
  21.  cc_pvh = ccmicrovolt_to_pvh(cc_voltage_uv);  
  22.  pr_debug("cc_pvh = %lld pico_volt_hour ", cc_pvh);  
  23.  cc_uah = div_s64(cc_pvh, chip->r_sense_uohm);  
  24.  *val = cc_uah;  
  25. }  

4:UUC:计算方法和UC一致,但是rbatt是动态变化的,会复杂点;
  1. static int calculate_termination_uuc(struct pm8921_bms_chip *chip,  
  2.      int batt_temp, int chargecycles,  
  3.     int fcc_uah, int i_ma,  
  4.     int *ret_pc_unusable)  
  5. {  
  6.  int unusable_uv, pc_unusable, uuc;  
  7.  int i = 0;  
  8.  int ocv_mv;  
  9.  int batt_temp_degc = batt_temp / 10;  
  10.  int rbatt_mohm;  
  11.  int delta_uv;  
  12.  int prev_delta_uv = 0;  
  13.  int prev_rbatt_mohm = 0;  
  14.  int prev_ocv_mv = 0;  
  15.  int uuc_rbatt_uv;  
  16.   
  17.   
  18.  for (i = 0; i <= 100; i++) {  
  19.   ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut,  
  20.     batt_temp_degc, i);  
  21.   rbatt_mohm = get_rbatt(chip, i, batt_temp);  
  22.   unusable_uv = (rbatt_mohm * i_ma) + (chip->v_cutoff * 1000);  
  23.   delta_uv = ocv_mv * 1000 - unusable_uv;  
  24.   
  25.   
  26.   pr_debug("soc = %d ocv = %d rbat = %d u_uv = %d delta_v = %d ",  
  27.     i, ocv_mv, rbatt_mohm, unusable_uv, delta_uv);  
  28.   
  29.   
  30.   if (delta_uv > 0)  
  31.    break;  
  32.   
  33.   
  34.   prev_delta_uv = delta_uv;  
  35.   prev_rbatt_mohm = rbatt_mohm;  
  36.   prev_ocv_mv = ocv_mv;  
  37.  }  
  38.   
  39.   
  40.  uuc_rbatt_uv = linear_interpolate(rbatt_mohm, delta_uv,  
  41.      prev_rbatt_mohm, prev_delta_uv,  
  42.      0);  
  43.   
  44.   
  45.  unusable_uv = (uuc_rbatt_uv * i_ma) + (chip->v_cutoff * 1000);  
  46.   
  47.   
  48.  pc_unusable = calculate_pc(chip, unusable_uv, batt_temp, chargecycles);  
  49.  uuc = (fcc_uah * pc_unusable) / 100;  
  50.  pr_debug("For i_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d uuc = %d ",  
  51.      i_ma, uuc_rbatt_uv, unusable_uv,  
  52.      pc_unusable, uuc);  
  53.  *ret_pc_unusable = pc_unusable;  
  54.  return uuc;  
  55. }  


 高通的这套BMS算法运行起来由于ocv的校准和温度等等原因,会有一定的偏差,高通还有一套通过校准OCV来估算SOC(简称soc_est)的机制,下面就是使用这套来校准SOC;


二:校准SOC
 
 高通算法通过对soc与soc_est比较计算出ocv的差值,来改变last_ocv_uv的值,主要是改变RC,重新计算soc,将会使得soc与soc_est越来越接近,越来越准;

 ocv在以下2种情况会被改变:

1:系统睡眠唤醒期间,cov被更新,库仑计RST;

                2:低电进入adjust_soc()方法调节;


    在高通8064平台由于电量计对大电流计算不准确,一直亮屏的情况(没有经历睡眠唤醒的ocv更新与CC RST)会导致关机电压到达3.74V。要想解决这个问题必须使得校准SOC可以正常工作。但是当满电时开机就会记录ocv的值偏高,导致快要低电时不能很好的校准soc。所以有必要在马上进入低电(15%)时做一次模拟开机一次(电量计RERST CC=0从soc找出ocv )使得last_ocv_uv降下来,才可以完美发挥adjust_soc的作用,使得关机电压能一直能到3.4V左右。
 
  1. <6>[ 7796.038269] read_soc_params_raw: 333333333 last_good_ocv_uv3777000uV  
  2.   
  3.   
  4. <6>[ 7796.038360] read_soc_params_raw: last_good_ocv_raw0x943flast_good_ocv_uv3777000uV  
  5.   
  6.   
  7. <6>[ 7796.038543] calculate_soc_params: FCC = 3190000uAh batt_temp = 300cycles = 0  
  8.   
  9.   
  10. <6>[ 7796.038635] calculate_remaining_charge_uah: ocv = 3777000 pc = 35  
  11.   
  12.   
  13. <6>[ 7796.038665] calculate_soc_params: RC = 1116500uAh  
  14.   
  15.   
  16. <6>[ 7796.038726] calculate_soc_params: cc_uah = 394979uAh raw->cc = 5764312  
  17.   
  18.   
  19. <6>[ 7796.038818] calculate_state_of_charge: RUC(RC-CC-UUC) = 657721uAh RC = 1116500uAh CC394979uAh UUC63800uAh FCC3190000uAh SOC(RUC/FCC-UUC) =21  

adjust_soc方法:

  1. </pre><p class="pa-1" style="line-height: 18px; font-size: 14px; padding-top: 0px; padding-bottom: 0px; margin-top: 0px; margin-bottom: 10px; color: rgb(68, 68, 68); font-family: 宋体;"><pre name="code" class="html"> static int last_soc_est = -EINVAL;  
  2.   
  3.   
  4.   
  5. static int adjust_soc(struct pm8921_bms_chip *chip, int soc,  
  6.   
  7.   
  8.   
  9.   int batt_temp, int chargecycles,  
  10.   
  11.   
  12.   
  13.   int rbatt, int fcc_uah, int uuc_uah, int cc_uah)  
  14.   
  15.   
  16.   
  17. {  
  18.   
  19.   
  20.   
  21.  int ibat_ua = 0vbat_uv = 0;  
  22.   
  23.   
  24.   
  25.  int ocv_est_uv = 0soc_est = 0pc_est = 0pc = 0;  
  26.   
  27.   
  28.   
  29.  int delta_ocv_uv = 0;  
  30.   
  31.   
  32.   
  33.  int n = 0;  
  34.   
  35.   
  36.   
  37.  int rc_new_uah = 0;  
  38.   
  39.   
  40.   
  41.  int pc_new = 0;  
  42.   
  43.   
  44.   
  45.  int soc_new = 0;  
  46.   
  47.   
  48.   
  49.  int m = 0;  
  50.   
  51.   
  52.   
  53.  int rc = 0;  
  54.   
  55.   
  56.   
  57.  int delta_ocv_uv_limit = 0;  
  58.   
  59.   
  60.   
  61.  int correction_limit_uv = 0;  
  62.   
  63.   
  64.   
  65.   
  66.   
  67.   
  68.   
  69.  rc = pm8921_bms_get_simultaneous_battery_voltage_and_current(  
  70.   
  71.   
  72.   
  73.        &ibat_ua,  
  74.   
  75.   
  76.   
  77.        &vbat_uv);  
  78.   
  79.   
  80.   
  81.  if (rc < 0) {  
  82.   
  83.   
  84.   
  85.   pr_err("simultaneous vbat ibat failed err = %d ", rc);  
  86.   
  87.   
  88.   
  89.   goto out;  
  90.   
  91.   
  92.   
  93.  }  
  94.   
  95.   
  96.   
  97.   
  98.   
  99.   
  100.   
  101.  very_low_voltage_check(chip, ibat_ua, vbat_uv);  
  102.   
  103.   
  104.   
  105.   
  106.   
  107.   
  108.   
  109.  if (chip->low_voltage_detect &&  
  110.   
  111.   
  112.   
  113.   wake_lock_active(&chip->low_voltage_wake_lock)) {  
  114.   
  115.   
  116.   
  117.   if (is_voltage_below_cutoff_window(chip, ibat_ua, vbat_uv)) {  
  118.   
  119.   
  120.   
  121.    soc = 0;  
  122.   
  123.   
  124.   
  125.    pr_info("Voltage below cutoff, setting soc to 0 ");  
  126.   
  127.   
  128.   
  129.    goto out;  
  130.   
  131.   
  132.   
  133.   }  
  134.   
  135.   
  136.   
  137.  }  
  138.   
  139.   
  140.   
  141.   
  142.   
  143.   
  144.   
  145.  delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);  
  146.   
  147.   
  148.   
  149.   
  150.   
  151.   
  152.   
  153.  ocv_est_uv = vbat_uv + (ibat_ua * rbatt)/1000;  
  154.   
  155.   
  156.   
  157.  calc_current_max(chip, ocv_est_uv, rbatt);  
  158.   
  159.   
  160.   
  161.  pc_est = calculate_pc(chip, ocv_est_uv, batt_temp, last_chargecycles);  
  162.   
  163.   
  164.   
  165.  soc_est = div_s64((s64)fcc_uah * pc_est - uuc_uah*100,  
  166.   
  167.   
  168.   
  169.       (s64)fcc_uah - uuc_uah);  
  170.   
  171.   
  172.   
  173.  soc_est = bound_soc(soc_est);  
  174.   
  175.   
  176.   
  177.   
  178.   
  179.   
  180.   
  181.  /* never adjust during bms reset mode */  
  182.   
  183.   
  184.   
  185.  if (bms_reset) {  
  186.   
  187.   
  188.   
  189.   pr_debug("bms reset mode, SOC adjustment skipped ");  
  190.   
  191.   
  192.   
  193.   goto out;  
  194.   
  195.   
  196.   
  197.  }  
  198.   
  199.   
  200.   
  201.   
  202.   
  203.   
  204.   
  205.  if (ibat_ua < 0 && pm8921_is_batfet_closed()) {  
  206.   
  207.   
  208.   
  209.   soc = charging_adjustments(chip, soc, vbat_uv, ibat_ua,  
  210.   
  211.   
  212.   
  213.     batt_temp, chargecycles,  
  214.   
  215.   
  216.   
  217.     fcc_uah, cc_uah, uuc_uah);  
  218.   
  219.   
  220.   
  221.   goto out;  
  222.   
  223.   
  224.   
  225.  }  
  226.   
  227.   
  228.   
  229.   
  230.   
  231.   
  232.   
  233.  /*  
  234.   
  235.   
  236.   
  237.   * do not adjust  
  238.   
  239.   
  240.   
  241.   * if soc_est is same as what bms calculated  
  242.   
  243.   
  244.   
  245.   * OR if soc_est > 15  
  246.   
  247.   
  248.   
  249.   * OR if soc it is above 90 because we might pull it low  
  250.   
  251.   
  252.   
  253.   * and  cause a bad user experience  
  254.   
  255.   
  256.   
  257.   */  
  258.   
  259.   
  260.   
  261.  if (soc_est == soc  
  262.   
  263.   
  264.   
  265.   || soc_est > 15  
  266.   
  267.   
  268.   
  269.   || soc >= 90)  
  270.   
  271.   
  272.   
  273.   goto out;  
  274.   
  275.   
  276.   
  277.   
  278.   
  279.   
  280.   
  281.  if (last_soc_est == -EINVAL)  
  282.   
  283.   
  284.   
  285.   last_soc_est = soc;  
  286.   
  287.   
  288.   
  289.   
  290.   
  291.   
  292.   
  293.  n = min(200, max(1 , soc + soc_est + last_soc_est));  
  294.   
  295.   
  296.   
  297.  /* remember the last soc_est in last_soc_est */  
  298.   
  299.   
  300.   
  301.  last_soc_est = soc_est;  
  302.   
  303.   
  304.   
  305.   
  306.   
  307.   
  308.   
  309.  pc = calculate_pc(chip, chip->last_ocv_uv,  
  310.   
  311.   
  312.   
  313.    chip->last_ocv_temp_decidegc, last_chargecycles);  
  314.   
  315.   
  316.   
  317.  if (pc > 0) {  
  318.   
  319.   
  320.   
  321.   pc_new = calculate_pc(chip, chip->last_ocv_uv - (++m * 1000),  
  322.   
  323.   
  324.   
  325.      chip->last_ocv_temp_decidegc,  
  326.   
  327.   
  328.   
  329.      last_chargecycles);  
  330.   
  331.   
  332.   
  333.   while (pc_new == pc) {  
  334.   
  335.   
  336.   
  337.    /* start taking 10mV steps */  
  338.   
  339.   
  340.   
  341.    m = m + 10;  
  342.   
  343.   
  344.   
  345.    pc_new = calculate_pc(chip,  
  346.   
  347.   
  348.   
  349.       chip->last_ocv_uv - (m * 1000),  
  350.   
  351.   
  352.   
  353.       chip->last_ocv_temp_decidegc,  
  354.   
  355.   
  356.   
  357.       last_chargecycles);  
  358.   
  359.   
  360.   
  361.   }  
  362.   
  363.   
  364.   
  365.  } else {  
  366.   
  367.   
  368.   
  369.   /*  
  370.   
  371.   
  372.   
  373.    * pc is already at the lowest point,  
  374.   
  375.   
  376.   
  377.    * assume 1 millivolt translates to 1% pc  
  378.   
  379.   
  380.   
  381.    */  
  382.   
  383.   
  384.   
  385.   pc = 1;  
  386.   
  387.   
  388.   
  389.   pc_new = 0;  
  390.   
  391.   
  392.   
  393.   m = 1;  
  394.   
  395.   
  396.   
  397.  }  
  398.   
  399.   
  400.   
  401.   
  402.   
  403.   
  404.   
  405.  delta_ocv_uv = div_s64((soc - soc_est) * (s64)m * 1000,  
  406.   
  407.   
  408.   
  409.        n * (pc - pc_new));  
  410.   
  411.   
  412.   
  413.   
  414.   
  415.   
  416.   
  417.  if (abs(delta_ocv_uv) > delta_ocv_uv_limit) {  
  418.   
  419.   
  420.   
  421.   pr_debug("limiting delta ocv %d limit = %d ", delta_ocv_uv,  
  422.   
  423.   
  424.   
  425.     delta_ocv_uv_limit);  
  426.   
  427.   
  428.   
  429.   
  430.   
  431.   
  432.   
  433.   if (delta_ocv_uv > 0)  
  434.   
  435.   
  436.   
  437.    delta_ocv_uv = delta_ocv_uv_limit;  
  438.   
  439.   
  440.   
  441.   else  
  442.   
  443.   
  444.   
  445.    delta_ocv_uv = -1 * delta_ocv_uv_limit;  
  446.   
  447.   
  448.   
  449.   pr_debug("new delta ocv = %d ", delta_ocv_uv);  
  450.   
  451.   
  452.   
  453.  }  
  454.   
  455.   
  456.   
  457.   
  458.   
  459.   
  460.   
  461.  if (wake_lock_active(&chip->low_voltage_wake_lock)) {  
  462.   
  463.   
  464.   
  465.   pr_debug("Low Voltage, apply only ibat limited corrections ");  
  466.   
  467.   
  468.   
  469.   goto skip_limiting_corrections;  
  470.   
  471.   
  472.   
  473.  }  
  474.   
  475.   
  476.   
  477.   
  478.   
  479.   
  480.   
  481.  if (chip->last_ocv_uv > 3800000)  
  482.   
  483.   
  484.   
  485.   correction_limit_uv = the_chip->high_ocv_correction_limit_uv;  
  486.   
  487.   
  488.   
  489.  else  
  490.   
  491.   
  492.   
  493.   correction_limit_uv = the_chip->low_ocv_correction_limit_uv;  
  494.   
  495.   
  496.   
  497.   
  498.   
  499.   
  500.   
  501.  if (abs(delta_ocv_uv) > correction_limit_uv) {  
  502.   
  503.   
  504.   
  505.   pr_debug("limiting delta ocv %d limit = %d ", delta_ocv_uv,  
  506.   
  507.   
  508.   
  509.     correction_limit_uv);  
  510.   
  511.   
  512.   
  513.   
  514.   
  515.   
  516.   
  517.   if (delta_ocv_uv > 0)  
  518.   
  519.   
  520.   
  521.    delta_ocv_uv = correction_limit_uv;  
  522.   
  523.   
  524.   
  525.   else  
  526.   
  527.   
  528.   
  529.    delta_ocv_uv = -1 * correction_limit_uv;  
  530.   
  531.   
  532.   
  533.   pr_debug("new delta ocv = %d ", delta_ocv_uv);  
  534.   
  535.   
  536.   
  537.  }  
  538.   
  539.   
  540.   
  541.   
  542.   
  543.   
  544.   
  545. skip_limiting_corrections:  
  546.   
  547.   
  548.   
  549.  chip->last_ocv_uv -delta_ocv_uv;  
  550.   
  551.   
  552.   
  553.   
  554.   
  555.   
  556.   
  557.  if (chip->last_ocv_uv >= chip->max_voltage_uv)  
  558.   
  559.   
  560.   
  561.   chip->last_ocv_uv = chip->max_voltage_uv;  
  562.   
  563.   
  564.   
  565.   
  566.   
  567.   
  568.   
  569.  /* calculate the soc based on this new ocv */  
  570.   
  571.   
  572.   
  573.  pc_new = calculate_pc(chip, chip->last_ocv_uv,  
  574.   
  575.   
  576.   
  577.    chip->last_ocv_temp_decidegc, last_chargecycles);  
  578.   
  579.   
  580.   
  581.  rc_new_uah = (fcc_uah * pc_new) / 100;  
  582.   
  583.   
  584.   
  585.  soc_new = (rc_new_uah - cc_uah - uuc_uah)*100 / (fcc_uah - uuc_uah);  
  586.   
  587.   
  588.   
  589.  soc_new = bound_soc(soc_new);  
  590.   
  591.   
  592.   
  593.   
  594.   
  595.   
  596.   
  597.  /*  
  598.   
  599.   
  600.   
  601.   * if soc_new is ZERO force it higher so that phone doesnt report soc=0  
  602.   
  603.   
  604.   
  605.   * soc = 0 should happen only when soc_est == 0  
  606.   
  607.   
  608.   
  609.   */  
  610.   
  611.   
  612.   
  613.  if (soc_new == 0 && soc_est >= the_chip->hold_soc_est)  
  614.   
  615.   
  616.   
  617.   soc_new = 1;  
  618.   
  619.   
  620.   
  621.   
  622.   
  623.   
  624.   
  625.  soc = soc_new;  
  626.   
  627.   
  628.   
  629.   
  630.   
  631.   
  632.   
  633. out:  
  634.   
  635.   
  636.   
  637.  pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, "  
  638.   
  639.   
  640.   
  641.   "soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, "  
  642.   
  643.   
  644.   
  645.   "pc_new = %d, soc_new = %d, rbatt = %d, m = %d ",  
  646.   
  647.   
  648.   
  649.   ibat_ua, vbat_uv, ocv_est_uv, pc_est,  
  650.   
  651.   
  652.   
  653.   soc_est, n, delta_ocv_uv, chip->last_ocv_uv,  
  654.   
  655.   
  656.   
  657.   pc_new, soc_new, rbatt, m);  
  658.   
  659.   
  660.   
  661.   
  662.   
  663.   
  664.   
  665.  return soc;  
  666.   
  667.   
  668.   
  669. }  

免责声明:文章转载自《针对高通BMS的研究 高通电量计》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇SpringBoot项目启动时执行初始化操作03-Android基础知识-04-Android 中 Activity 启动模式下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

RN8302b调试笔记

RN8302b调试笔记 艰难调试完毕,吐槽的话就不说了,只有个手册,官方应用笔记还不给,想想太窝火 无奈才某处花钱买了个官方应用笔记。。。。。 忽然发现,现在啥都要钱了,希望博客园的分享可以坚持下去! 1. 通讯接口        别的不说了,既然PCB都做了,那就一个坑一个坑的来吧,这里不再赘述SPI的的接口以及通讯协议问题,这部分还都是靠谱的,按要求做...

linux查看主机最后启动时间

1.使用who查看最后重启时间 who -b 2.使用last查看近几次重启时间 last reboot 第一条即为最后一次重启日期 last其实是查询用户的登录记录,reboot是一个伪用户;也就是你可以通过last查询其他用户的登录记录。 参考: http://www.linuxidc.com/Linux/2014-09/106489.htm...

【Unity】伪线框渲染Shader的实现

线框Shader的渲染在游戏应用上还是有一定的需求,这次分享一个伪的线框渲染Shader。之所以称之为伪线框,是因为真正的线框应该渲染的是线,这在常规上是使用几何体着色器输出线段图元来实现。但是几何体着色器是DirectX 10的特性,所以针对移动平台,如果有少量线框渲染需求的,这个实现方法的门槛更低。 先说一下实现的原理:通过模型UV的边界来实现线框的渲...

高通创始人复盘30年发展历程

美国西海岸时间12月12日,在圣迭戈高通总部,我们见到了久违的艾文·雅各布(Irwin Jacobs,以下简称艾文)。   这是一位已经被写入传奇的老人。   最早,他在大学学习的是酒店管理,但只学了一年半,就果断转学电子工程,最终在麻省理工学院取得博士学位后留校任教。1968年,他辞去做了13年的老师工作,与两位同伴共同创业,成立了一家知名的技术咨询公司...

Cesium深入浅出之阴影贴图【转】

引子 又偷懒了,说好的周更的,又拖了一个月咯。前面两篇写了可视域分析和视频投影,无一例外的都用到了ShadowMap也就是阴影贴图,因此觉得又必要单独写一篇阴影贴图的文章。当然了,还有另外一个原因,文章中视频投影是利用Cesium自带的Entity方式实现的,毫无技术性可言,在文章结尾我说了可以使用ShadowMap方式来做,原理类似于可视域分析,那么今天...

PostgresQL中的NUlls first/last功能

Nulls first/last功能简介Nulls first/last功能主要用于order by排序子句中,影响空值Null在排序结果中的位置。简单来说,Nulls first表示Null值在排序时一直排在所有值的前面,也就是处理order by a desc时PostgresQL执行器认为Null值大于所有值,而order by a或order by...