TechForum分享者:yinxiangxv
关键词:驱动,IO,ADC,开发板
第一步:NCV80490硬件电路搭建和负载驱动
要让上面的模块能够正常工作首先要提供一个稳定的直流电压12V。并且要明确每个引脚的功能和定义:
VD (电源): 这是芯片的功率电源输入。
连接: 将这两个引脚(VD)直接连接到汽车电池的正极 (V_BAT, 通常为12V)。
设计要点: 在VD和GND之间放置一个100nF的陶瓷电容作为退耦电容,尽可能靠近芯片引脚。这能滤除电源噪声,保证芯片稳定工作。
GND (地): 芯片的参考地。
连接: 连接到系统的地平面 (GND)。
OUT (输出): 功率输出端。
连接: 将这两个引脚(OUT)连接到负载 Z_L 的一端。负载的另一端连接到系统地 (GND)。
IN (输入): 逻辑控制输入。
连接: 连接到MCU的一个GPIO输出引脚。MCU通过输出高电平(例如5V或3.3V)来命令芯片导通,输出低电平(0V)来命令芯片关断。
设计要点: 可以在MCU和IN引脚之间串联一个小电阻(如 1kΩ),用于限流保护MCU的GPIO口。
CS_EN (电流检测使能): 诊断功能使能。
连接: 连接到MCU的另一个GPIO输出引脚。MCU输出高电平来激活电流检测和所有诊断功能,输出低电平来关闭它们以降低功耗。
CS (电流检测): 模拟反馈输出。
连接: 连接到MCU的一个ADC(模数转换器)输入引脚。
设计要点: 这是最关键的外围电路设计。
在CS引脚和GND之间连接一个精密电阻 R_CS。芯片输出的检测电流 I_CS 流过这个电阻,产生一个MCU可以测量的电压 V_CS。R_CS 的值需要根据最大负载电流和MCU的ADC量程来计算(具体见应用案例)。
在CS引脚和GND之间并联一个小电容 C_CS(如 1nF)。它与 R_CS 组成一个RC低通滤波器,滤除噪声,为ADC提供一个稳定的信号。
如果上面的12v满足之后就要进行控制了。
按照这个思路进行:
. 简易原理图描述:
电源部分: V_BAT或者12v直流电源 → NCV84090的 VD 引脚。
负载部分: NCV84090的 OUT 引脚 → 负载 Z_L → GND。
控制部分 (MCU):
MCU GPIO_1 → 1kΩ电阻 → IN 引脚。
MCU GPIO_2 → CS_EN 引脚。
MCU ADC_1 → CS 引脚。
反馈部分: CS 引脚 → R_CS → GND。同时,CS 引脚 → C_CS → GND。
根据上面提到的控制部分,刚好手边有一个开发板,是创龙的t113,正好用来进行控制的逻辑。
为此要实现这些函数:
1. 驱动函数接口 (API) 设计:
void NCV84090_Init(pin_IN, pin_CS_EN, pin_CS): 初始化函数,配置MCU的GPIO和ADC引脚。
void NCV84090_TurnOn(): 打开开关。内部实现是将 IN 引脚置为高电平。
void NCV84090_TurnOff(): 关闭开关。内部实现是将 IN 引脚置为低电平。
void NCV84090_EnableDiagnostics(bool enable): 使能/禁能诊断。控制 CS_EN 引脚。
float NCV84090_ReADCurrent(): 核心功能,读取并返回以安培(A)为单位的负载电流。
FaultType NCV84090_CheckFault(): 诊断函数,返回当前检测到的故障类型(如正常、开路、短路等)。
2. 核心函数实现逻辑:
NCV84090_ReADCurrent() 实现:
确保开关已导通 (IN = High) 且诊断已使能 (CS_EN = High)。
等待稳定时间! 根据数据手册,从 IN 变高到 CS 信号稳定需要一个延迟 t_CS_High2。程序必须等待超过这个时间。
通过MCU的ADC读取 CS 引脚的电压 V_CS。
计算检测电流: I_CS = V_CS / R_CS。
查表计算真实电流: NCV84090的电流检测比 K 是非线性的。需要根据数据手册中的 K vs I_OUT 曲线创建一个查找表。通过 I_CS 估算 I_OUT 范围,然后在表中查找或插值得到准确的 K 值。
计算最终电流: I_OUT = I_CS * K。
返回 I_OUT。
NCV84090_CheckFault() 实现:
导通状态故障:
读取 V_CS 电压。如果 V_CS 远高于正常工作范围,达到了一个固定的故障电压 V_CS_FAULT,则说明发生了过流、短路或过热故障。
关断状态开路故障:
执行诊断序列:将 IN 置低,CS_EN 置高。
等待延迟 t_d_OL_off。
读取 V_CS 电压。如果此时 V_CS 被拉高到 V_CS_FAULT,则说明发生了开路负载故障。
第二步:确定T113上使用的GPIO
你需要查看你的 T113 开发板的原理图或引脚分布图,找到一个空闲的、可以作为 GPIO 输出的引脚。全志的芯片引脚命名通常是 P + 组号 + 序号,例如 PB5 (第B组的第5个引脚) 或 PG13 (第G组的第13个引脚)。
假设我们选择了 PB5 这个引脚。
第三步:在T113的Linux系统中控制GPIO
在 root@T113-Tronlong:~# 这个运行环境中,控制 GPIO 最直接、最标准的方法是通过 Linux 的 sysfs GPIO 接口。这是一种通过读写虚拟文件来控制硬件的方式,非常方便。
以下是完整的命令行操作步骤:
1. 导出 (Export) GPIO
首先,你需要告诉内核,你想要使用 PB5 这个引脚。你需要计算出它的编号。
GPIO 编号计算公式: (组号 ASCII – ‘A’) * 32 + 序号
对于 PB5: (‘B’的ASCII – ‘A’的ASCII) * 32 + 5 → (1) * 32 + 5 = 37
现在,导出这个GPIO:
echo 37 > /sys/class/gpio/export
执行成功后,系统会创建一个新的目录 /sys/class/gpio/gpio37。
2. 设置GPIO方向 (Direction)
接下来,你需要将这个GPIO设置为输出模式。
echo “out” > /sys/class/gpio/gpio37/direction
3. 控制GPIO输出高低电平
现在,你可以通过向 value 文件写入 1 或 0 来控制GPIO输出高电平或低电平了。
输出高电平 (3.3V) → 导通NCV84090 → 负载工作:
echo 1 > /sys/class/gpio/gpio37/value
当你执行这条命令时,T113的PB5引脚会输出高电平,NCV84090的IN引脚收到高电平,其内部开关导通,OUT引脚被拉到地,电流流过你的负载,负载开始工作(例如,继电器吸合,LED点亮)。
输出低电平 (0V) → 关断NCV84090 → 负载停止:
echo 0 > /sys/class/gpio/gpio37/value
当你执行这条命令时,PB5引脚输出低电平,NCV84090的IN引脚收到低电平,其内部开关断开,OUT引脚变为高阻态,负载上没有电流流过,负载停止工作。
4. (可选) 释放 (Unexport) GPIO
当你不再使用这个GPIO时,可以将其释放,让其他程序使用。
echo 37 > /sys/class/gpio/unexport
第四步:编写Shell脚本实现自动化控制
你可以把上述命令写到一个Shell脚本里,方便调用。例如,创建一个名为 control_load.sh 的脚本:
如何使用这个脚本:
在T113上,用vi control_load.sh创建并编辑这个文件,把代码粘贴进去。
给脚本添加执行权限: chmod +x control_load.sh
打开负载: ./control_load.sh on
关闭负载: ./control_load.sh off
这样,就成功地在你的T113运行环境中,通过编程(Shell脚本)的方式,利用GPIO控制了外部的NCV84090芯片,并驱动了一个12V的负载。
上面的代码是通过shell代码的方式进行运行的。不过也可以通过c代码的方式进行。

下面看看相关函数的c代码如何实现:
使用C语言(推荐用于最终产品)
这是最正规、最高效的方式。我们需要利用Linux提供的系统调用来访问sysfs接口。
1. 环境准备
你的T113开发板的Linux系统里通常已经内置了C语言的交叉编译工具链(gcc)或者你可以通过opkg或apt安装它。
2. C语言代码实现
下面是一个C语言实现的示例,它基本实现了你设计的API。
文件: ncv84090_driver.c
# 运行编译好的程序
这样的就必须要启用虚拟机的环境进行编译,为此还是用这个shell代码比较直接:

后面的测试等下次再进行吧,这个逻辑上和硬件的连线上面没什么问题,调试没什么问题,应该就可以连接硬件后运行代码,并也可以配合示波器进行检测。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
// — 定义GPIO编号 (根据你的硬件连接修改) —
// 假设 IN -> PB5 -> 37
// 假设 CS_EN -> PG13 -> 205
#define PIN_IN_NUM 37
#define PIN_CS_EN_NUM 205
// — 全局变量存储引脚编号 —
static int pin_IN = -1;
static int pin_CS_EN = -1;
// ADC引脚通常通过IIO子系统访问,这里简化处理
// static int pin_CS = -1;
// — 底层GPIO操作函数 —
int gpio_export(int pin) {
char buffer[64];
int fd = open(“/sys/class/gpio/export”, O_WRONLY);
if (fd < 0) {
perror(“Failed to open export for writing”);
return -1;
}
int len = snprintf(buffer, sizeof(buffer), “%d”, pin);
if (write(fd, buffer, len) < 0) {
// Ignore error if already exported
}
close(fd);
return 0;
}
int gpio_set_direction(int pin, const char* dir) {
char path[64];
snprintf(path, sizeof(path), “/sys/class/gpio/gpio%d/direction”, pin);
int fd = open(path, O_WRONLY);
if (fd < 0) {
perror(“Failed to open direction for writing”);
return -1;
}
if (write(fd, dir, strlen(dir)) < 0) {
perror(“Failed to set direction”);
close(fd);
return -1;
}
close(fd);
return 0;
}
int gpio_set_value(int pin, int value) {
char path[64];
snprintf(path, sizeof(path), “/sys/class/gpio/gpio%d/value”, pin);
int fd = open(path, O_WRONLY);
if (fd < 0) {
perror(“Failed to open value for writing”);
return -1;
}
char val_str = (value == 1) ? ‘1’ : ‘0’;
if (write(fd, &val_str, 1) != 1) {
perror(“Failed to write value”);
close(fd);
return -1;
}
close(fd);
return 0;
}
// — ADC读取函数 (这是一个简化的示例) —
// 在真实的T113系统中,你需要通过IIO (Industrial I/O)子系统来读取ADC
// 通常是读取 /sys/bus/iio/devices/iio:deviceX/in_voltageY_raw 文件
float read_ADC_voltage() {
// 伪代码: 实际实现会更复杂
int raw_value;
float voltage;
char buffer[16];
// 假设ADC在iio:device0的通道1
int fd = open(“/sys/bus/iio/devices/iio:device0/in_voltage1_raw”, O_RDONLY);
if (fd < 0) {
perror(“Failed to open ADC raw file”);
return -1.0;
}
read(fd, buffer, sizeof(buffer)-1);
close(fd);
raw_value = atoi(buffer);
// 假设ADC是12位(4096),参考电压是1.8V
voltage = (float)raw_value / 4095.0 * 1.8;
printf(“ADC Raw: %d, Voltage: %.3fV\n”, raw_value, voltage);
return voltage;
}
// — API 实现 —
/**
* @brief 初始化NCV84090驱动
* @note 在Linux sysfs中,不需要传入pin号,硬编码或从配置文件读取
*/
void NCV84090_Init() {
pin_IN = PIN_IN_NUM;
pin_CS_EN = PIN_CS_EN_NUM;
printf(“Initializing NCV84090 driver…\n”);
gpio_export(pin_IN);
gpio_export(pin_CS_EN);
gpio_set_direction(pin_IN, “out”);
gpio_set_direction(pin_CS_EN, “out”);
// Set initial state
NCV84090_TurnOff();
NCV84090_EnableDiagnostics(false);
printf(“Initialization complete.\n”);
}
/**
* @brief 打开开关
*/
void NCV84090_TurnOn() {
gpio_set_value(pin_IN, 1);
}
/**
* @brief 关闭开关
*/
void NCV84090_TurnOff() {
gpio_set_value(pin_IN, 0);
}
/**
* @brief 使能/禁能诊断
*/
void NCV84090_EnableDiagnostics(bool enable) {
gpio_set_value(pin_CS_EN, enable ? 1 : 0);
}
/**
* @brief 读取并返回以安培(A)为单位的负载电流
*/
float NCV84090_ReADCurrent() {
// 1. 确保开关导通且诊断使能
NCV84090_TurnOn();
NCV84090_EnableDiagnostics(true);
// 2. 等待稳定时间 (t_CS_High2), 例如 200微秒
usleep(200);
// 3. 读取CS引脚电压
float v_cs = read_ADC_voltage();
if (v_cs < 0) {
return -1.0; // Read error
}
// — 简化处理,实际需要查找表 —
// 假设R_CS = 1kΩ, 那么 I_CS = V_CS / 1000
// 假设在工作点 K ≈ 5000 (需要查数据手册)
float i_cs = v_cs / 1000.0;
float k_ratio = 5000.0;
float i_out = i_cs * k_ratio;
// 操作完成后可以关闭诊断以降低功耗
NCV84090_EnableDiagnostics(false);
return i_out;
}
// FaultType NCV84090_CheckFault() { … } // 故障诊断函数实现类似
// — 主函数:测试API —
int main() {
NCV84090_Init();
printf(“Turning ON the load…\n”);
NCV84090_TurnOn();
sleep(2); // 保持开启2秒
printf(“Reading current…\n”);
float current = NCV84090_ReADCurrent();
if (current >= 0) {
printf(“Measured Load Current: %.3f A\n”, current);
} else {
printf(“Failed to read current.\n”);
}
sleep(1);
printf(“Turning OFF the load…\n”);
NCV84090_TurnOff();
return 0;
}
想参与一起讨论?欢迎到TechForum原文下留言:
【DigiKey免单狂欢】NCV84090DR2G软硬件设计-1
【DigiKey免单狂欢】NCV80490DR2G软硬件设计-2
【DigiKey免单狂欢】NCV84090DR2G软硬件设计-3
帖文内容源自DigiKey与21电源网 – 电子研习社的活动:

想知道更多不同活动资讯,请到热点活动页面了解更多→



