|

楼主 |
发表于 2023-5-10 13:37:17
|
显示全部楼层
本帖最后由 飞向狙沙 于 2023-5-10 14:42 编辑
开关机电路使用了这个电路,按下开关Q1导通,上电开机,此时单片机完成初始化,需要给POWER_C一个高电平,Q2导通拉低Q1 G极保持开机状态,此时开关SW1可以作为普通开关正常使用,当需要关机时按下开关,单片机检测到按键按下按照设定规则(长按1s)判定关机时拉低POWER_C,此时Q2截止,但是开关还按下,所以Q1还是导通,直到开关松开,G极高电平实现物理关机,这个开关机电路可以实现关机0损耗,缺点就是关机后单片机断电,不能自我唤醒,不过这里自我唤醒也没啥意义
内阻采样电路部分,原理可参考运放恒流电路求解 - 电子学堂 数码之家 (mydigit.cn)里@qrut 和@邓穿石 给出的讲解,U5.1、R31、R32、R34、R35、R36组成一个10mA(实测9mA左右)恒流源,R34为恒流源的采样电阻,具体恒流值计算方法可以参考下上边帖子的10楼,交流恒流经过C23、C24反接组成的无极电容隔直后对被测电阻放电,恒流值定为Io,被测电阻定位Ro,则按照欧姆定律在被测电阻上产生一个交流电压信号Uo=Io*Ro,C22隔直电容经过隔直通交把这个电压信号导入到U5.2的同相端进行同相放大,因为这个交流信号包含负压,单电源的运放处理不了,经过R24、R27叠加一个3.3/2=1.65V的偏置电压。由R20、R26组成放大(100k+400)/400=250倍放大,当Q3导通时R20、R21、R22并联成一个33.33k电阻,放大倍数变为(33.33k+400)/400=84.33倍放大。
这是短接时的adc波形,效果已经不太好了,不知道是不是运放的问题
这是200mΩ时的波形
关键元件
R34为恒流源采样电阻,需要高精度低温漂
R31/32/35/36设定恒流值
C23/24反向链接组成一个无极电容,耐压翻倍,容量减半,容量需要100uF以上,这里我使用了25V220uF电容,等效电容耐压50V,有点低了,测不了50V电池,我也用不到
C22为采样隔直电容,耐压需要50V或超过期望被测电压值,用MLCC实际应该不太合适,这里最好使用X7R或者CBB?这块不太了解,大佬给解释一下呗
R25/30作为被测电池电压分压电阻,分压比(200k+12k)/12k=17.67倍,最高测量3.3V*17.67=58V电压
R20/21/22/26为运放放大倍率设定电阻
电压测量范围由C22/23/24和R25/30共同决定
算法相关
使用了平均值滤波,三个点做一次平均,然后后移一位继续三点平均
- void ADC_avgfilter(uint16_t *arr)
- {
- uint16_t temp = arr[0];
- for (uint16_t i = 1; i < ADC_SAMPLES - 1; i++)
- {
- arr[i] = (temp + arr[i] + arr[i + 1]) / 3;
- temp = arr[i];
- }
- }
复制代码
阻值计算没太搞明白FFT,而且这里是用的方波,感觉也用不到FFT,直接从滤波之后的数据里取波峰波谷差值计算了
- // 获取阻值
- float ADC_getResistance()
- {
- // 波峰波谷求和,求平均值
- uint16_t peakCount = 0, troughCount = 0;
- uint32_t peakSum = 0, troughSum = 0;
- uint32_t avg = 0;
- uint16_t adcs[ADC_SAMPLES];
- // 数据采集
- adc_read(RESISTANCE_ADC_PIN, adcs);
- // 数据滤波
- ADC_avgfilter(adcs);
- // 求平均值,简单求波峰波谷
- for (int i = 0; i < ADC_SAMPLES; i++)
- {
- avg += adcs[i];
- }
- avg /= ADC_SAMPLES;
- // 开始位置是正半周还是负半周:正半周1,负半周-1
- int8_t currentCycle = adcs[0] > avg ? 1 : -1;
- // 当前周期的波峰波谷
- int16_t currentPeak, currentTrough;
- if (currentCycle > 0)
- currentPeak = adcs[0];
- else
- currentTrough = adcs[0];
- // 求波峰波谷
- for (int i = 1; i < ADC_SAMPLES - 1; i++)
- {
- // 正半周切换负半周
- if (currentCycle > 0 && adcs[i] < avg)
- {
- currentTrough = adcs[i];
- currentCycle = -1;
- peakSum += currentPeak;
- peakCount++;
- }
- // 负半周切换正半周
- else if (currentCycle < 0 && adcs[i] > avg)
- {
- currentPeak = adcs[i];
- currentCycle = 1;
- troughSum += currentTrough;
- troughCount++;
- }
- // 正半周
- else if (currentCycle > 0 && adcs[i] > avg && currentPeak < adcs[i])
- {
- currentPeak = adcs[i];
- }
- // 负半周
- else if (currentCycle < 0 && adcs[i] < avg && currentTrough > adcs[i])
- {
- currentTrough = adcs[i];
- }
- // Serial.printf("adc:%d\r\n", adcs[i]);
- }
- peakSum /= peakCount;
- troughSum /= troughCount;
- Serial.printf("peakSum:%d\r\n", peakSum);
- Serial.printf("troughSum:%d\r\n", troughSum);
- float r = 1.0 * (peakSum - troughSum) / 2;
- Serial.print("r:");
- Serial.print(r);
- Serial.println();
- r = r * ADC_getVcc() / ADC_RANGE / CONSTANT_CURRENT / MAGNIFICATION_RATE1;
- Serial.print("R:");
- Serial.print(r);
- Serial.println();
- return r;
- }
复制代码
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
打赏
-
查看全部打赏
|