资讯动态

ATT7022EU三相电能计量芯片参考驱动深度解析:从SPI读写到校表实战

发布时间:2026/9/7 9:21:02 来源:尧图企业网站定制
简介面向国网三相电能计量芯片的参考驱动包适合智能电表、电力监测终端等嵌入式开发人员参考。驱动以完整源代码和工程形式组织可直接基于平台移植解决三相电能计量底层通信、寄存器配置与数据读取等常见开发问题降低从芯片手册到实际代码的落地门槛。压缩包共251个文件大小6.27MB核心内容为52个C源文件与43个头文件同时包含Keil工程配置、编译链接产物及备份文件便于对照源码理解编译流程与工程组织。已有2257人学习下载。读者可从中获取芯片驱动框架、外设操作函数、工程模板与排错线索源码模块化程度较高易于裁剪和二次开发尤其适合正在做该芯片产品开发或驱动移植的嵌入式工程师快速上手。 好先说一个很多人拿到那把“ATT7022EU参考驱动”时都会有的疑问代码不是都给了吗对着芯片手册抄一遍不就行了但等你真正把板子焊好、准备通过SPI把三相电压电流读回来的时候各种奇怪问题就冒出来了——读回来的数据要么是全0要么是乱码就算电压正常了电流又怎么校都不对更别提电能量老是和标准表对不上。这些问题的答案往往不在压缩包里的某一行代码而在于你对这颗三相电能计量芯片本身的理解深度。这篇文章就围绕国网三相电能计量芯片ATT7022EU及其配套参考驱动程序展开从头梳理芯片工作原理、代码结构、寄存器操作、校表流程和常见坑点。如果你正在做三相智能电表、能源管理终端、充电桩/储能并网柜的电力采样或者只是被分配去“把参考驱动跑通”这篇内容应该能帮你省下不少调板子的时间。1. 项目背景打开驱动包之前先想清楚ATT7022EU到底替你干了什么活1.1 一颗计量芯片和“自己写算法”的路线差了多少很多刚接触电力仪表的人第一次看到ATT7022EU这种专用计量芯片时会有一个本能反应不就是一个ADC吗我用MCU自带ADC加一套算法不也能算电压电流这种想法不能算错但做出来之后你会发现那是一条非常长的路。三相电能计量要做的事情远比“采样然后算个RMS”复杂。它需要同时采集三相电压、三相电流共六个通道的模拟信号做AD转换后还要计算电压有效值、电流有效值、有功功率、无功功率、视在功率、功率因数、频率、相角还要完成有功电能的积分、四象限无功电能计算、需量统计甚至还包括失压检测、断相检测、相序判断、启动电流判断和潜动判断。如果这些都用MCU的通用ADC加软件算法来做光是写算法、调滤波、做校准开发周期就得按几个月算而且精度和温漂很难控制到国网表需要的水平。ATT7022EU这种专用计量芯片把上面这些全做进了芯片内部的DSP里。主控MCU要做的事情只是通过SPI或者UART把芯片算好的结果读出来再配置一下校表参数和启潜动阈值。从“自己算”变成“读结果”开发效率完全不是一个量级。我见过一个很直观的方案选型对比这里直接列成表格方便你对照自己的项目情况来选实现路线开发周期精度与稳定性校表复杂度适用场景专用计量芯片ATT7022EU短MUC代码量小高芯片内部算法经过批量验证配置增益/相位寄存器即可智能电表、电力终端、量产产品高精度ADC MCU算法长需要自己搞定滤波和算法取决于算法写得好不好温漂风险高需要自己设计整套校准体系实验研究、非标定制、教学集成计量核的SoC短但绑定特定平台高通常用原厂工具平台选型受限的场景从我经手的项目来看只要不是特殊到必须自研算法的场景选ATT7022EU这类专用计量芯片是最务实的路线。尤其是国网招标的表计项目芯片成熟度、大全温度范围内的稳定性、出厂的一致性直接决定了你的产品能不能过检测。1.2 标题里的“国网”两个字为什么值得留意“国网三相电能计量芯片”这个叫法不是随便加的。国网智能电表有几个很典型的技术特征要支持正向/反向有功计量要支持四象限无功要按尖峰平谷分时计费要支持冻结和结算还要记录失压、断相等事件。这些需求直接映射到ATT7022EU的寄存器设计上比如有些寄存器专门存四象限无功电能有些寄存器给出失压状态标志有些校表参数专门对应启动电流和潜动阈值。所以你会发现参考驱动里不只是“读电压、读电流”那么简单。它往往还包含失压判断逻辑、相序判断逻辑、电能量读取时先锁存再读取的处理以及一套和DL/T 645这类通讯协议对接时可用的数据映射思路。这些内容对做终端、充电桩、储能并网柜的人同样有价值哪怕你不需要走国网协议里面的数据读取顺序和事件判定方法也是通用的。这也是我比较推荐大家别跳过参考驱动、直接自己从手册摸寄存器的一个原因。原厂驱动里的每个函数基本上都对应一类实际问题你把这些函数背下来没意义但弄懂它们为什么这么写能少踩很多隐蔽的坑。1.3 参考驱动的定位它是参考答案不是交付物拿到一个参考驱动最容易犯的错是把里面的main函数直接当成自己产品的业务逻辑模板然后东拼西凑就开始堆代码。参考驱动的价值是有限的它能给你的是芯片的寄存器读写函数怎么封装、上电初始化的顺序是什么、校表参数和计量数据之间的关系怎么组织、SPI时序和校验和算法长什么样。它不能给你的是你产品里的采样电路抗干扰设计、你的电源纹波控制、你的存储策略、你的协议栈和事件记录机制更不会帮你把三相电压电流的采样通道校准到0.2级。说句直接的话参考驱动最大的作用是“交叉验证”。你把参考实现拿过来和自己的写法逐行比对验证SPI时序、校验和算法、初始化顺序这些硬骨头是不是一致比单纯看手册效率高得多。更重要的是当你的板子读数异常时有一套已知能跑的参考实现做对照你能更快确定问题出在硬件还是软件。2. 动手第一步参考驱动包里的代码到底该怎么看、怎么改2.1 常见的驱动包文件结构这个压缩包和我见过的大多数电能计量芯片参考驱动结构类似解压后一般是下面这几类内容平台适配文件比如spi_driver.c、gpio_config.c负责MCU的SPI外设初始化、片选脚控制、延时函数有些包还会带上位机打印或者调试串口的初始化芯片驱动文件比如att7022_driver.c和att7022_driver.h里面是寄存器读写函数、校验和计算函数、软复位函数、初始化函数以及读取电压、电流、功率、电能等参数的封装函数演示工程通常是带main函数的示例工程演示上电初始化、轮询读取数据并打印方便你在真实板子上快速跑通校表说明或者上位机工具有些包会附带一个简单的校表流程文档或者配合上位机读取误差寄存器、下发增益参数的协议说明。这里要提醒一句不同渠道流出的参考驱动版本可能对应不同的MCU平台。有的基于STM32标准库有的基于HAL库还有的是基于某款M0内核的国产MCU。代码风格和底层接口差别很大但芯片寄存器读写逻辑基本一致。移植时要重点关注芯片相关的那部分平台相关代码大概率是要重写的。2.2 按“三层”拆解驱动移植就不慌我习惯把这种参考驱动拆成三个层次来读这样的好处是移植时心里有数知道每一层改动的时候会影响什么。第一层是MCU平台适配层。这里SPI初始化函数、CS片选控制、延时函数全部是和硬件绑定的。你拿到驱动后第一件事就是把spi_write_byte、spi_read_byte这类函数替换成自己板子上的实现把片选引脚定义改成你自己的GPIO。这层的坑主要在于SPI模式必须和芯片手册要求一致包括时钟极性和相位稍后我会细说。第二层是芯片驱动层。寄存器读写函数、校验和算法、软复位函数、芯片初始化函数基本都在这一层。这一层理论上和MCU平台无关但要注意参考驱动默认的头文件里可能有一些宏定义比如ATT7022_CS_LOW()、ATT7022_CS_HIGH()、delay_us()它们在上层代码里被大量调用移植时不要只改了底层函数就完事要把这些宏一一对应好。第三层是业务层。它会根据寄存器读出来的原始值换算成工程值比如把电压原始值乘以0.1变成伏特把电流原始值乘上某个系数变成安培再组合成功率、电能等数据。有些参考驱动还会在这一层实现简单的失压判断、相序判断这部分的代码逻辑可以借鉴但通常需要结合你的产品需求来调整。这里多说一句调试期间强烈建议在第二层加一个“寄存器读写日志”开关把每次读写的地址、数据、校验和全部通过串口打印出来。我遇到过一种很隐蔽的问题电压、电流看起来读得都对但电能量偶尔不涨后来抓日志才发现是某一次读操作里校验和只做了8位累加、没有截断处理导致数据偶尔错一位。这种问题如果没有日志排查起来要人命。2.3 移植时最容易被忽略的两个改动点第一个是SPI的速率和模式。参考驱动里可能写了一个给指定芯片用的spi_init但它的时钟分频不一定适合你的MCU主频。ATT7022EU对SPI时序有上限要求具体的频率上限要看芯片手册但经验上是不要一上来就拉满SPI时钟。频率太高时信号完整性和片选时序都可能出问题设备工作不稳定。我的习惯是先用一个比较保守的速率比如1MHz左右跑通功能再逐步提高测到稳定读数据为止。第二个是延时函数和软复位时序。不同平台参考驱动用的延时单位可能不一样有的用delay_ms(100)有的用Delay_Ms(2)如果你直接照搬可能上电初始化还没完成就开始读数据导致第一次读数全是0xFF。另外复位脉冲的宽度、发完命令后CS拉高的时间点都需要对照芯片手册重新核对一遍。大多数“第一次读数据失败”的问题最后查下来都是这里的时间参数不对。3. 核心机制逐项拆解读写时序、上电初始化和自动校表3.1 SPI寄存器读写命令字、校验和与容易写错的地方ATT7022EU这一代计量芯片的SPI从机通信协议有一个很重要的特点每次读或者写命令字节和校验和的位置是有讲究的。命令字节的最高位用来表示读还是写低7位是寄存器地址。读命令最高位置1写命令最高位清0寄存器地址范围通常不超过0x7F。数据读回时先回3个字节的寄存器数据最后跟一个字节的校验和。校验和是把命令字节、地址和数据字节做累加只取低8位。下面我写一个非常简化的示意代码帮助你理解这类芯片的SPI读流程// 示意代码读取ATT7022EU一个计量数据寄存器 uint8_t att_read_register(uint8_t addr, uint32_t *value) { uint8_t cmd (0x80) | (addr 0x7F); // 读标志 寄存器地址 uint8_t checksum 0; uint32_t raw 0; uint8_t b1, b2, b3; cs_low(); spi_write_byte(cmd); b1 spi_read_byte(); b2 spi_read_byte(); b3 spi_read_byte(); checksum spi_read_byte(); cs_high(); if (checksum ! (uint8_t)(cmd b1 b2 b3)) { return 1; // 校验失败 } raw ((uint32_t)b1 16) | ((uint32_t)b2 8) | b3; *value raw; return 0; }注意这是一个示意实现不同版本的参考驱动在字节序、校验和计算细节上可能有差异具体以你手上驱动包和对应芯片手册为准。这个看似简单的流程实际项目里有三个高频踩坑点。第一个是没有把命令字节的最高位置对读操作和写操作搞反读出来自然不对。第二个是校验和把地址和数据顺序加错了或者忘了加命令字节本身导致数据明明对但校验永远失败。第三个是CS信号的电平时序没处理好在发送命令之前没有把CS拉低或者读写完之后没有等芯片输出完成就拉高CS导致最后一个字节的数据读不到。3.2 上电后为什么一定要重写校表参数再软复位这恐怕是新手最容易忽略的一个环节。ATT7022EU这类计量芯片的校表参数保存在芯片内部的RAM里上电后默认参数是可以让芯片裸跑的但精度不会太好。这些参数掉电就会丢所以每次上电MCU都必须按照自己保存的校表配置把对应的增益、相位、启动电流、潜动阈值这些参数重新写进芯片然后再做一次软件复位让芯片按照新的参数重新启动计量。这个流程的顺序非常关键先写参数再软复位最后等芯片复位完成并稳定后再开始读数据。如果顺序反了先复位后写参数那么有些参数可能没生效因为芯片内部状态机已经把初始配置锁定了如果写完参数不复位部分寄存器虽然显示写入成功但实际计量流程可能还在用旧配置。所以参考驱动里往往是最后一步调用软复位函数这个函数名可能是att7022_soft_reset或者att70222_reboot建议确认一下你的驱动包里有没有这个步骤。补充一个细节上电后不要立刻操作芯片。芯片本身的电源上电稳定、晶振起振和内部上电复位都需要时间。经验做法是MCU上电后延时几十毫秒到一百毫秒以上再开始对ATT7022EU做初始化。这个延时参数参考驱动里通常有但有时会被默认的初始化顺序吞掉移植时要注意。3.3 把人工校表变成可复现的自动校表流程校表这件事听起来很高端本质其实就是一个闭环修正过程用三相标准源给芯片提供一个已知的高精度信号然后读取芯片测量结果对比标准源的值把误差值换算成增益或相位补偿量写回芯片的校表寄存器反复迭代几次直到误差满足要求。参考驱动里通常只提供了单次读误差、写增益这类函数不会把完整的校表流程给你写出来。实际做校表程序时我建议按下面的逻辑来组织第一步设置标准源输出额定电压、额定电流频率50Hz功率因数1.0作为校表的基准点第二步读取芯片的电压通道误差和电流通道误差先校电压通道再校电流通道避免相互耦合第三步把误差值换算成增益寄存器的新值写入后读取回读值确认写成功了第四步在功率因数0.5L或者0.8C的负载点下读取相位误差修正相位补偿寄存器第五步重复第二步到第四步直到误差小于目标值比如有功误差小于0.2%。这里有一个很重要的工程经验电压通道和电流通道的校准系数要分开保存小信号和大信号最好分开校。因为电流互感器在小电流时非线性比较明显如果你只在校表点5A下校好了到了0.5A或者0.05A可能误差会变大。所以很多厂家的校表流程会设置多个电流点分别求出不同区间下的校正系数再按分段方式使用。参考驱动里不一定有这个分段的逻辑但你的产品如果要过检这块工作基本跑不掉。4. 数据读取与计量精度这个地方卡住多半是位权或者符号问题4.1 数据寄存器的“位权换算”是第一步ATT7022EU读回来的原始值都是定点数要变成真正能用的电压、电流、功率必须乘以对应的位权。常见的位权关系大概是这样的因为芯片版本和寄存器不同具体数值还是以你手上手册和驱动头文件里的宏定义为准数据项常见位权说明电压有效值0.1V / LSB原始值除以10得到伏特电流有效值0.000625A / LSB或者类似量级看寄存器定义有功功率0.0005W或更小三相合并后要注意是否叠加功率因数0.001或0.0001有些寄存器直接是定点小数频率0.01Hz / LSB读出来除以100得到Hz相角0.01度 / LSB多用于相序判断最容易出错的地方是直接把原始值当成工程值来写逻辑。比如你读到一个电压原始值2285如果直接当2285V那肯定是错的正确结果是228.5V。这种错误在自测阶段可能因为数值“看起来像”而被忽略一旦接入上位机或者计量平台单位不一致的问题就会暴露。我的习惯是做一个统一的数据换算层所有从寄存器读出的raw数据都通过这个换算层转换成double类型的工程值模块内部只使用工程值不让“裸数据”到处跑。这样改一个位权只需要改一处排查问题时也只需要关注原始值到工程值的映射关系。4.2 电能量锁存、符号位和三相一致性读电能量寄存器和读电压电流有一个非常大的区别电能量的累加是一个持续进行的过程如果芯片在更新电能量计数器时你正好在读某个寄存器就有可能读到“新旧混合”的数据而且三相之间可能不一致。为了避免这个问题绝大多数计量芯片都提供了“锁存”功能。你先发一个电能量锁存命令芯片内部会把当前各相电能量同时复制到一组锁存缓冲区里之后你再去读A相、B相、C相的电能拿到的就是同一个时间断面的快照。这里特别提醒锁存命令和读数据不能离得太远。有些项目里中断里发锁存命令主循环里隔了几百毫秒再去读这段时间里芯片有可能又进行了一次新的锁存导致你读到的还是不一致的数据。更稳妥的做法是发锁存命令后立刻连续读取A、B、C三相对应寄存器中间不要穿插其他耗时的操作。另一个高频坑是符号位处理。功率寄存器里是有符号的正向用电时读出来是正数反向送电或者用户光伏反送时读出来是负数。如果你忽略符号位把负数强制转成uint32再去做电能累加结果就会变成很大的正数累积电量瞬间就爆了。正确做法是先把读回来的补码转成有符号数再根据正负方向分别累加到正向电能或者反向电能里。国网表对正反向有功、四象限无功都有明确规定这一块驱动里通常只给了原始寄存器数据业务判断逻辑一定要自己写对。4.3 启动电流、潜动和失压阈值怎么配才不闹心启潜动和失压阈值这类参数配置得好不好直接影响计量数据能不能被现场接受。启动电流阈值设太大用户家里小功率用电器开着走着慢甚至不走字设太小线路上的零漂或者互感器励磁电流又会造成无谓的微小电量累积。我见过一个现场问题一台三相表一切正常但每个月总有千分之几度的电量在半夜波动查到最后就是启动电流阈值设得太低互感器的微小零漂被计成了能量。配置这些参数时单位一定要换算对。芯片手册里启动电流阈值一般按照“相对于额定电流Ib的百分比”来写比如0.1%Ib你要根据实际互感器变比转换成寄存器里要填的原始值。失压判定也是类似通常按“电压有效值跌落到额定电压Un的百分之多少”来触发而且还要配合一个连续持续时间避免电压瞬间跌落造成误报。这些参数参考驱动里通常只是给了默认值甚至有的驱动包把这些参数放在一个配置结构体里方便你按订单修改。上线前务必核对它们的单位和量纲千万不能拿着别家的配置表直接灌进去。4.4 读取周期多长才合适ATT7022EU内部的计量数据是按固定的周期更新的SPI读取本身并不会改变芯片的计算节奏。如果你的主控用一个不确定的周期去循环读数据有时能读到新的数据有时又读回上一帧的旧数据这对于需要和标准表做误差比对的测试来说会非常难受。实际项目我建议用固定定时器触发读取任务比如每100ms读一组三相电压、电流、功率和电能保持读取周期稳定。不要直接在while循环里加一个不严格的延时去读。定时间隔的选择也不需要太短因为计量数据的更新频率通常远小于SPI速率读得太频繁除了增加总线占用没什么实际意义。从功能调试角度先按100ms固定周期读精度比对没问题后再考虑缩短。5. 常见问题与排查实录5.1 SPI读回全0、全0xFF或者偶尔蹦一个错值这是最基础也最常遇到的问题。全0和全0xFF通常是通信层面的故障先看SPI模式。ATT7022EU这类芯片对时钟极性和相位有要求很多MCU库里默认的SPI模式不一定匹配。排查方法是用示波器或者逻辑分析仪抓CS、SCLK、MOSI和MISO四根线对着芯片手册上的时序图看一遍重点看CS在SCLK空闲时是高电平还是低电平、数据是在上升沿还是下降沿采样。只要有一根线的电平关系不对就永远是同样的错误。如果波形看起来对但数据还是不对把SPI时钟降到手册规定的频率以下排除高速信号干扰。如果降速后正常说明布线或者干扰有问题需要从电源、接地和信号完整性上找原因。还有一个容易忽略的点就是芯片的复位引脚。如果复位引脚被外部看门狗或者别的GPIO拉着芯片一直处于复位态SPI自然怎么读都是空的。先把复位引脚的电平和时序测量清楚再往下查。5.2 电压正常但电流偏小、为0或者小信号乱跳电压通道正常说明芯片的电源、SPI通信和基本配置已经没问题了问题多半出在电流通道的硬件或者参数配置上。先排查硬件电流互感器的二次侧有没有和芯片的电流采样通道正确连接采样电阻和滤波电容有没有贴错规格电流互感器的输出方向如果接反可能导致读出来的电流很小甚至反符号。排除硬件后就是软件配置。重点检查电流通道的通道增益寄存器有没有被正确配置尤其是初始值是否被默认参数覆盖了。然后看启动电流阈值如果芯片认为当前电流低于启动阈值会直接按0处理表现出来就是小电流时读数不变化。小信号乱跳的问题则更多出现在采样回路的抗干扰上我建议先检查芯片电流采样输入端的RC滤波再看PCB布局模拟地和数字地是不是分开了采样线有没有和电源线长距离平行走线。5.3 校表误差怎么都收敛不了校表误差收不下去时先别急着反复调增益寄存器。我最常见到的原因有三个第一个是标准源本身的稳定度不够输出信号自己就在抖芯片怎么校都追不上第二个是校表的功率因数点不对比如你要校相位却选在功率因数1.0的纯阻性点相位误差根本体现不出来第三个是校表过程中SPI一直被其他任务打断某次写增益寄存器校验失败没被发现实际值没有写进去。建议把校表流程做成一个独立任务在校表期间关闭或暂停其他高优先级中断每次写完增益之后回读校验确认寄存器值和目标值一致后再进入下一轮迭代。校表误差最终收敛还有一个前提就是信号源和被校表之间要共地接线端子接触电阻不能太大尤其是电流回路哪怕有几毫欧的接触电阻在小信号下都会引入不小误差。5.4 电能量不累加/丢脉冲的几种原因电能量不累加这个问题看起来很可怕但排查方向其实很集中。先确认芯片是否真正进入计量状态有没有初始化完成校表参数有没有正确下发芯片有没有意外进入复位。然后看启动电流阈值和潜动阈值过高的阈值会导致电流小的时候直接冻结能量寄存器。再看电能寄存器读取的时候有没有按“先锁存、再依次读三相”的顺序来如果锁存后读取的时间跨度太长可能在旧断面和新断面之间发生了跳变。如果电能偶尔累加但点数不对先查SPI读取的数据有没有偶发校验失败。校验失败的处理策略很重要参考驱动通常返回一个错误码但很多应用代码拿到错误码之后只是打印一下并没有重读。如果你的校验算法在临界状态下偶尔出错电能就会漏记一段数据。建议在业务层对关键数据的读操作做“失败重读”机制一次校验失败连续重读连续多次失败再报错比单次失败就放弃要可靠得多。5.5 几个排除异常的土办法聊完上面这些具体问题最后分享几个我经常用的土办法特别适合现场快速定位。第一个是逻辑分析仪抓SPI波形不要只看现象去猜。抓的时候以CS信号作为触发源会比较清晰地看到完整的读写包结构一眼就能看出命令字节、数据字节和校验字的位置关系。第二个是把芯片寄存器读到的原始值和手册上的“上电默认值”做对比。上电后不去修改任何参数读几个关键寄存器和手册默认值对不上基本就能说明通信或者芯片自检出了问题。第三个是对芯片做一次软复位如果复位后数据恢复说明之前的状态不对是某个寄存器被写坏了需要回查配置流程。这些方法都不需要多高深的仪器但能让排查效率提高一倍不止。6. 复盘把一个参考驱动真正用起来我的几点体会6.1 拿到参考驱动以后我建议你这样“抄”参考驱动这东西我见过两种极端用法。一种是把demo工程原封不动搬到自己板子上只改引脚定义结果一跑就各种问题另一种是觉得参考代码写得乱自己从头按手册写结果连校验和算法都写错。我的建议是取一个中间态先按参考驱动的原始流程原样跑通用串口把芯片数据打印出来确认芯片本身没有问题然后再逐步把平台适配层换成自己项目里的代码换成一层、验证一层不要一次性推翻重写。这样每一步出问题都知道是哪个改动引入的。6.2 真正决定计量稳定性的地方往往不在驱动代码里最后聊一个容易被忽略的体会。参考驱动里每一行代码都有据可查反而是驱动之外的硬件设计才是决定一个电能计量产品最终能不能量产的胜负手。电源纹波、模拟采样通道的滤波、互感器选型、PCB布局、地平面设计这些因素对计量精度的影响比驱动代码大得多。我调试过一块板子SPI和寄存器配置全部正常电压电流数据也能读出来就是小信号漏记问题解决不了后来重新画了一版PCB把电流采样回路的地线重新规划问题直接消失。驱动代码只是让芯片“跑起来”电路设计才决定它“跑得好不好”。这一点在你说服团队重视硬件设计时非常管用。如果你手里也有一份类似的参考驱动包不妨先按照“看结构、跑通流程、换平台层、再改业务”的节奏走一遍把芯片手册和实际代码对着看而不是直接拿demo上量产品。等你在示波器上看到那几根SPI波形和手册波形完全重合的时候这颗芯片对你来说就没有秘密了。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价