i.MX 功率分析:三范围智能电流传感器 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
注意:在从聚合器插入或拔出智能传感器时,务必切断目标板和聚合器的电源。 注意:请参阅此链接以了解配备智能传感器的电路板。 本页记录了三范围“智能”电流传感器,它是用于分析应用板上电源的大型系统的一部分。该智能传感器采用带有三个电流检测放大器的 Kinetis KL05Z。它允许测量三个范围内的电流。四种组装选项允许测量轨道电压 0-3.3V(两个总电流范围)、0-6.6V 和 12V。它连接到一个聚合器,该聚合器为多个智能传感器板供电、控制和聚合数据。 与旧式双范围测量系统相比,最大的改进之一是传感器上的微控制器允许几乎同时测量电路板上的所有仪表轨道。双量程分析仪每次只能进行一次测量。 它们旨在与微控制器板一起使用,充当触发器和数据聚合器。该聚合器还可用于重新编程传感器。 使用 4 点探头和 Keysight B2902B SMU 测量时,智能传感器在运行模式(最高电流范围)下增加的串联电阻低于 11 毫欧姆。 可以通过定期触发测量并以图形方式呈现结果来制作“功率示波器”...... 示意图: 电路板布局,顶部: 电路板布局,底部: 这是两张照片,其中还包含一枚镍币,以显示比例。该板的尺寸约为 0.5 x 1.3 英寸。 连接: 智能传感器接头连接如下: 5V:为 3.3V 稳压器供电,进而为传感器板上的其他所有部件供电 12V:所有开关场效应晶体管(FET)的栅极均上拉至12V GND:接地 SCL/TX:I2C时钟线 SDA/RX: I2C数据线 SWD_CLK:用于触发智能传感器进行测量的线 RESET_B:用于重置智能传感器板的线 SWD_IO:智能传感器选择线 工作原理: 三个分流器和电流检测放大器用于测量三个范围内的电流。一个分流器/感测放大器对具有集成到 IC 封装中的 0.002 Ω分流器 (U1,INA250)。另外两个感测放大器(U2 和 U3、INA212)需要外部分流器。 FET Q1、Q2 和 Q3 用于切换电路中的两个较低范围的分流器/感测放大器对。在正常运行操作(最高电流范围)中,Q1(FDMC012N03,Rds(on) 低于1.5mΩ )导通,短路后电路中仅剩 U1。FET Q4、Q5 和 Q6 将电压转换为 3.3V,以便 U4(MCU KL05Z)上的 GPIO 可以控制它们。 通过电阻器 R3、R4、R12 和 Q7 可以实现轨电压测量。并非所有这些都存在于每个装配选项中。用于测量轨道电压 0-3.3V,R12 已填充。要测量0-6.6V,R3、R4 和 Q7 已填充。打开时,Q7 会启用分压器。所有装配选项填充信息均可在示意图(附件)中找到。 调节器 U5(AP2210N)为电路板上的所有组件提供 3.3V 电源。该 1% 容差调节器用于为 U4 中的 ADC 提供良好的参考。 微控制器 U4 通过电阻器 R9、R10 和 R11 检测电路板的组装填充选项,以便可以在所有类型的传感器板上使用相同的应用程序代码。GPIO 控制 FET,四个 ADC 通道用于测量感测放大器输出和轨电压。传感器板上配备微控制器可让用户进行额外的操作,例如计数库仑,并允许所有类似仪器的轨道通过触发线 SWD_CLK 同时进行测量。数据通信可以通过 I2C 或 UART 进行,因为这两个引脚可以同时执行这两项功能。但是如果要将多个传感器板与聚合器一起使用,则需要通过 I2C 进行通信。 申请代码: 智能传感器上 KL05Z 的最新应用程序代码位于此处: https://os.mbed.com/users/r14793/code/30847-SMRTSNSR-KL05Z/ 。最新的二进制文件附在下面。 为了重新刷新智能传感器,需要进行聚合器页面中详细说明的修改。修改完成后,让聚合器断电,同时将 SWD 调试器插入 J5,将要编程的智能传感器插入 JP15。 非常古老的基于 UART 的 KL05Z 应用程序代码,内置在线 MBED 编译器(请注意,它需要修改内部振荡器的 mbed 库)。该代码用于测试第一个智能传感器原型。它现在已被废弃。如果用户想要使用插入 JP15 的单个传感器和 UART 分线连接器 J6,则会在此处发布。 /******************************************************************************
*
* MIT License (https://spdx.org/licenses/MIT.html)
* Copyright 2017-2018 NXP
*
* MBED code for KL05Z-based "smart" current sensor board, basic testing
* of functions via UART (connected via FRDM board and OpenSDA USB virtual
* COM port).
*
* Eventual goal is to have each smart sensor communicate over I2C to an
* aggregator board (FRDM board with a custom shield), allowing 1-10 power
* supply rails to be instrumented. Extra credit effort is to support
* sensors and aggregator with sigrok...
*
* Because there is no crystal on the board, need to edit source mbed-dev library
* to use internal oscillator with pound-define:
* change to "#define CLOCK_SETUP 0" in file:
* mbed-dev/targets/TARGET_Freescale/TARGET_KLXX/TARGET_KL05Z/device/system_MKL05Z4.c
*
******************************************************************************/
#include "mbed.h"
// These will be GPIO for programming I2C address...
// not yet implemented, using as test pins...
DigitalOut addr0(PTA3);
DigitalOut addr1(PTA4);
DigitalOut addr2(PTA5);
DigitalOut addr3(PTA6);
// configure pins for measurements...
// analog inputs from sense amps and rail voltage divider...
AnalogIn HIGH_ADC(PTB10);
AnalogIn VRAIL_ADC(PTB11);
AnalogIn LOW1_ADC(PTA9);
AnalogIn LOW2_ADC(PTA8);
// outputs which control switching FETs...
DigitalOut VRAIL_MEAS(PTA7); // turns on Q7, connecting voltage divider
DigitalOut LOW_ENABLE(PTB0); // turns on Q4, turning off Q1, enabling low measurement
DigitalOut LOW1(PTB2); // turns on Q5, turning off Q2, disconnecting shunt R1
DigitalOut LOW2(PTB1); // turns on Q6, turning off Q3, disconnecting shunt R2
// input used for triggering measurement...
// will eventually need to be set up as an interrupt so it minimizes delay before measurement
InterruptIn trigger(PTA0); // use as a trigger to make measurement...
// PTB3/4 can be used as UART or I2C...
// For easier development with one smart sensor, we are using UART here...
Serial uart(PTB3, PTB4); // tx, rx
long int count=0;
int n=25; // global number of averages for each measurement
int i, temp;
bool repeat=true; // flag indicating whether measurements should repeat or not
const float vref = 3.3; // set vref for use in calculations...
float delay=0.25; // default delay between measurement
bool gui = false; // flag for controlling human vs machine readable output
bool statistics = false;// flag for outputting min and max along with average (GUI mode only)
void enableHighRange(){
LOW_ENABLE = 0; // short both low current shunts, close Q1
wait_us(5); // delay for FET to settle... (make before break)
LOW1 = 0; LOW2 = 0; // connect both shunts to make lower series resistance
VRAIL_MEAS = 0; // disconnect rail voltage divider
wait_us(250); // wait for B2902A settling...
}
void enableLow1Range(){
LOW1 = 0; LOW2 = 1; // disconnect LOW2 shunt so LOW1 can measure
wait_us(5); // delay for FET to settle... (make before break)
LOW_ENABLE = 1; // unshort low current shunts, open Q1
VRAIL_MEAS = 0; // disconnect rail voltage divider
wait_us(250); // wait for B2902A settling...
}
void enableLow2Range(){
LOW1 = 1; LOW2 = 0; // disconnect LOW1 shunt so LOW2 can measure
wait_us(5); // delay for FET to settle... (make before break)
LOW_ENABLE = 1; // unshort low current shunts, open Q1
VRAIL_MEAS = 0; // disconnect rail voltage divider
wait_us(500); // wait for B2902A settling...
}
void enableRailV(){
VRAIL_MEAS = 1; // turn on Q7, to enable R3-R4 voltage divider
wait_us(125); // wait for divider to settle...
// Compensation cap can be used to make
// voltage at ADC a "square wave" but it is
// rail voltage and FET dependent. Cap will
// need tuning if this wait time is to be
// removed/reduced.
//
// So, as it turns out, this settling time and
// compensation capacitance are voltage dependent
// because of the depletion region changes in the
// FET. Reminiscent of grad school and DLTS.
// Gotta love device physics...
}
void disableRailV(){
VRAIL_MEAS = 0; // turn off Q7, disabling R3-R4 voltage divider
}
// this function measures current, autoranging as necessary
// to get the best measurement...
void measureAuto(){
Timer t;
float itemp;
float tempI=0;
float imin = 1.0; // used to keep track of the minimum...
float imax = 0; // used to keep track of the maximum...
t.start(); // use timer to see how long things take...
enableHighRange(); // this should already be the case, but do it anyway...
for (i = 0; i < n; i++){
itemp = HIGH_ADC; // read HIGH range sense amp output
if (statistics && itemp>imax) imax = itemp; // update max if necessary
if (statistics && itemp<imin) imin = itemp; // update min if necessary
tempI += itemp; // add current sample to running sum
}
tempI = tempI/n *vref/0.8; // compute average we just took...
if (gui) uart.printf("=> %5.3f ", tempI);
if (statistics && gui) uart.printf("[%5.3f/%5.3f] ", imin*vref/0.8, imax*vref/0.8);
// if current is below this threshold, use LOW1 to measure...
if (tempI < 0.060) {
if (!gui) uart.printf("... too Low: %f A, switching to low1 ==>\r\n", tempI);
tempI=0;
enableLow1Range(); // change FETs to enable LOW1 measurement...
imin = 1.0; imax = 0;
for (i = 0; i < n; i++){
itemp = LOW1_ADC; // read LOW1 sense amp output
if (statistics && itemp>imax) imax = itemp; // update max if necessary
if (statistics && itemp<imin) imin = itemp; // update min if necessary
tempI += itemp; // add current sample to running sum
}
tempI = tempI/n *vref/0.05/1000; // compute average we just took...
if (gui) uart.printf("%6.4f ", tempI);
if (statistics && gui) uart.printf("[%6.4f/%6.4f] ", imin*vref/0.05/1000, imax*vref/0.05/1000);
// if current is below this threshold, use LOW2 to measure...
if (tempI < 0.0009){
if (!gui) uart.printf("... too Low: %f A, switching to low2 ==>\r\n", tempI);
tempI=0;
enableLow2Range(); // change FETs to enable LOW1 measurement...
imin = 1.0; imax = 0;
for (i = 0; i < n; i++){
itemp = LOW2_ADC; // read LOW2 sense amp output
if (statistics && itemp>imax) imax = itemp; // update max if necessary
if (statistics && itemp<imin) imin = itemp; // update min if necessary
tempI += itemp; // add current sample to running sum
}
tempI = tempI/n *vref/2/1000; // compute average we just took...
if (gui) uart.printf("%8.6f ", tempI);
if (statistics && gui) uart.printf("[%8.6f/%8.6f] ", imin*vref/2/1000, imax*vref/2/1000);
}
}
t.stop(); // stop the timer to see how long it took do do this...
enableHighRange();
if (!gui) uart.printf("\r\nCurrent = %f A Current Measure Time = %f sec\r\n", tempI, t.read());
}
// the autoranging should really be done with functions that return values, as should the
// functions below... This would make for shorter and more elegant code, but the author
// is a bit of a pasta programmer...
void measureHigh(){
float highI=0;
enableHighRange();
for (i = 0; i < n; i++){
highI += HIGH_ADC;
}
highI = highI/n;
uart.printf("HIghI = %f A\r\n", vref*highI/0.8);
}
void measureLow1(){
float low1I=0;
enableLow1Range();
for (i = 0; i < n; i++){
low1I += LOW1_ADC;
}
enableHighRange();
low1I = low1I/n;
uart.printf("low1I = %f A\r\n", vref*low1I/0.05/1000);
}
void measureLow2(){
float low2I=0;
enableLow2Range();
for (i = 0; i < n; i++){
low2I += LOW2_ADC;
}
enableHighRange();
low2I = low2I/n;
uart.printf("low2I = %f A\r\n", vref*low2I/2/1000);
}
// measure the rail voltage, default being with
// a divide by 2 resistor divider
// It has to be switched out when not in use or it will
// add to the measured current, at least in the low ranges...
void measureRailV(){
float railv=0;
float mult = vref*2; // since divide by 2, we can measure up to 6.6V...
float vmin = 5; float vmax = 0;
float vtemp;
enableRailV(); // switch FETs so divider is connected...
for (i = 0; i < n; i++){
vtemp = VRAIL_ADC; // read voltage at divider output...
if (statistics && vtemp>vmax) vmax = vtemp; // update max if necessary
if (statistics && vtemp<vmin) vmin = vtemp; // update min if necessary
railv += vtemp; // add current sample to running sum
}
disableRailV(); // now disconnect the voltage divider
railv = railv/n; // compute average (note this is in normalized ADC [0..1])
// Convert to voltage by multiplying by "mult"
if (!gui) uart.printf("RailV = %5.3f V ", mult*railv);
if (gui) uart.printf("%5.3f ", mult*railv);
if (statistics && gui) uart.printf("[%5.3f/%5.3f] ", mult*vmin, mult*vmax);
uart.printf("\r\n");
}
// not sure how useful this function is...
void measureAll(){
measureHigh();
measureLow1();
measureLow2();
measureRailV();
}
// test function to see if trigger pin is being hit...
// intended for use later to do timed triggering of measurements...
void triggerIn(){
uart.printf("You're triggering me! \r\n");
measureAll();
}
// main...
int main()
{
// set up basic conditions...
Timer m;
uart.baud(115200);
enableHighRange(); // default state - only HIGH sense amp in circuit, no divider
// signal that we're alive...
uart.printf("Hello World!\r\n");
// configure the trigger interrupt...
trigger.rise(&triggerIn);
while (true) {
count++;
wait(delay);
if (repeat){ // if repeat flag is set, keep making measurements...
m.reset(); // reset and start timer...
m.start();
measureAuto(); // measuring current using auto-ranging...
measureRailV(); // measure rail voltage...
m.stop(); // stop the timer.
if (!gui) uart.printf(" Total Measure Time = %f sec", m.read());
if (!gui) uart.printf("\r\n\r\n");
}
// see if there are any characters in the receive buffer...
// this is how we change things on the fly...
// Commands (single keystroke... it's easier)
// t = one shot automeasure
// v = measure volt
// h = one shot high measure
// k = one shot LOW1 measure
// l = one shot LOW2 measure (letter l)
// r = toggle repeat
// R = turn off repeat
// + = faster repeat rate
// - = slower repeat rate
// = = set repeat rate to 0.25 sec
// g = use human readable text output
// G = use compressed text format for GUI
// s = turn statistics output off
// S = turn statistics output on (only in GUI mode)
// n = decrease number of averages for each measurement
// N = increase number of averages for each measurement
//
// these were for testing FET switching...
// 1 = LOW_ENABLE = 0 (the number 1)
// 2 = LOW1 = 0
// 3 = LOW2 = 0
// 4 = VRAIL_MEAS = 0
// ! = LOW_ENABLE = 1
// @ = LOW1 = 1
// # = LOW2 = 1
// $ = VRAIL_MEAS = 1
if (uart.readable()){
temp = uart.getc();
if (temp==(int) 't') {
if (!gui) uart.printf("Keyboard trigger: ");
measureAuto();
measureRailV();
//measureAll();
}
if (temp==(int) 'v') {
uart.printf("Keyboard trigger: ");
measureRailV();
}
if (temp==(int) 'h') {
uart.printf("Keyboard trigger: ");
measureHigh();
}
if (temp==(int) 'k') {
uart.printf("Keyboard trigger: ");
measureLow1();
}
if (temp==(int) 'l') {
uart.printf("Keyboard trigger: ");
measureLow2();
}
if (temp==(int) '1') {
LOW_ENABLE = 0;
uart.printf("Keyboard trigger: LowEnable = %d\r\n", 0);
}
if (temp==(int) '2') {
LOW1 = 0;
uart.printf("Keyboard trigger: LOW1 = %d\r\n", 0);
}
if (temp==(int) '3') {
LOW2 = 0;
uart.printf("Keyboard trigger: LOW2 = %d\r\n", 0);
}
if (temp==(int) '4') {
VRAIL_MEAS = 0;
uart.printf("Keyboard trigger: VRAILMEAS = %d\r\n", 0);
}
if (temp==(int) '!') {
LOW_ENABLE = 1;
uart.printf("Keyboard trigger: LowEnable = %d\r\n", 1);
}
if (temp==(int) '@') {
LOW1 = 1;
uart.printf("Keyboard trigger: LOW1 = %d\r\n", 1);
}
if (temp==(int) '#') {
LOW2 = 1;
uart.printf("Keyboard trigger: LOW2 = %d\r\n", 1);
}
if (temp==(int) '$') {
VRAIL_MEAS = 1;
uart.printf("Keyboard trigger: VRAILMEAS = %d\r\n", 1);
}
if (temp==(int) 'r') {
repeat = !repeat;
uart.printf("Keyboard trigger: repeat toggle: %s \r\n", repeat ? "true" : "false");
}
if (temp==(int) 'R') repeat = false;
if (temp==(int) '+') {
delay -= 0.05;
if (delay<0.05) delay = 0.05;
}
if (temp==(int) '-') {
delay += 0.05;
if (delay>1) delay = 1;
}
if (temp==(int) '=') delay = 0.25;
if (temp==(int) 'g') gui = false;
if (temp==(int) 'G') gui = true;
if (temp==(int) 's') statistics = false;
if (temp==(int) 'S') statistics = true;
if (temp==(int) 'n') {
n -= 25;
if (n<25) n = 25;
}
if (temp==(int) 'N') {
n += 25;
if (n>1000) n = 1000;
}
if (temp==(int) 'N' || temp==(int) 'n') uart.printf("/r/n/r/n Averages = %d \r\n\r\b", n);
}
}
} i.MX 8 系列 | i.MX 8QuadMax (8QM) | 8QuadPlus i.MX 8M | i.MX 8M Mini | i.MX 8M Nano i.MX6_全部 i.MX6DL i.MX6 双核 i.MX6DualPlus6QuadPlus i.MX6 四核 i.MX6S i.MX6SL i.MX6SoloX i.MX6UL i.MX7 双核 i.MX7 单核 i.MX7ULP PMIC
View full article