STM32(七):ADC电位检测 (标准库函数)

前言

上一篇文章已经介绍了如何用STM32单片机中的定时器的PWM波来实现LED的“呼吸”。这篇文章我们来介绍一下如何用STM32单片机中ADC进行电位检测,并发送到XCOM串口中显示。

一、实验原理

1.ADC模数转换的介绍

首先,我们先介绍一下AD模数模块(Analog-to-Digital Converter)。AD模数模块即模拟/数字转换器,主要功能是将连续变化的模拟信号转换为离散的数字信号。

由于单片机只能处理数字信号,因此,在对外部的模拟信号进行分析、处理的过程中,必须使用ADC模块将外部的模拟信号转换成单片机所能处理的数字信号。

A/D转换器的主要类型有以下几种:

  • 积分型
  • 逐次比较型
  • 并行比较型
  • \Sigma -\bigtriangleup调制型
  • 电容阵列逐次比较型
  • 压频变换型
A/D转换器的主要技术指标主要有以下几种:
  • 转换范围 :A/D转换器能够转换的模拟电压范围。
  • 分辨率:输出二进制数的位数表示,位数越多,分辨率越高。
  • 绝对精度:对应一个给定数字量的理论模拟输入与实际输入之差。
  • 转换速度:A/D转换器完成一次转换所需的时间。转 换时间是指从接到转换控制信号开始,到输出端得到稳定的 数字输出信号所经过的这段时间。

典型ADC结构性能比较如下:

2.STM32中的ADC

STM32拥有1~3个ADC(STM32F101/102系列只有1个ADC,STM32F103系列最少都拥有2个ADC ),这些ADC可以独立使用,也可以使用双重模式(提高采样率)。

STM32的ADC是12位逐次逼近型的模拟数字转换器。它有18个通道,可测量16个外部和2个内部信号源(温度传感器、内部参考电压)。ADC的输入时钟不得超过14MHz,其时钟频率由PCLK2分频产生。

ADC 特点
  • STM32单片机有2个独立的ADC控制器,有18个通道,可测量16个外部信号和2个内部信号源:内部温度传感器和内部参考电压(Bandgap voltage) 。
  • ADC 供电要求: 2.4V to 3.6 V 。
  • ADC 输入范围: VREF- ≤ VIN ≤ VREF+ (VREF+ and VREF- available only in LQFP100 package)
  • 精度:12位。结果可按左对齐或右对齐的方式存放在16位寄存器中。
  • A/D转换的过程:采用、保持、量化、编程。采样时间越长,转换结果越稳定。采样时间可设置为:1.5个/7.5个/13.5个/28.5个ADC时钟周期。
  • ADC转换时间 : 采用时间+转换时间(12.5个时钟周期)
  • DMA 功能 (ADC1)

要想搞清楚ADC的原理,那么首先还是先要学会看图,原理图如下图所示:

1.电压输入范围

ADC所能测量的电压范围就是VREF- ≤ VIN ≤ VREF+,把 VSSA 和 VREF-接地,把 VDDA和VREF+接 3.3V,得到ADC的输入电压范围为: 0~3.3V。

2.输入通道

STM32 16 个外部 ADC 通道可分为两组 : 规则的和注入的。
  • 每个组可以是这16个通道中的任意一些通道以任意顺序进行的组合。
  • 规则组最多有16个通道,通道和转换顺序在ADC规则系列寄存器x(ADC_JSQR)中选择。
  • 注入组最多有4个通道。通道和转换顺序在ADC注入系列寄存器(ADC_JSQR)中选择。

各通道的A/D 转换可以单次、连续、扫描或间断模 式执行, stm32f103zet6通道如下:

3.转换顺序

由于规则转换通道只有一个数据寄存器,使用多个通道进行转换需要考虑转换顺序。多个通道的使用顺序分为俩种情况:规则通道的转换顺序和注入通道的转换顺序。

可以看下参考博客,这边就不多加赘述。

4.触发源

触发有两种方式:配置寄存器和通过内部定时器或者外部IO触发转换。

  • 配置寄存器触发,通过配置控制寄存器CR2的ADON位,写1时开始转换,写0时停止转换。在程序运行过程中只要调用库函数,将CR2寄存器的ADON位置1就可以进行转换。
  • 通过内部定时器或者外部IO触发转换,也就是说可以利用内部时钟让ADC进行周期性的转换,也可以利用外部IO使ADC在需要时转换,具体的触发由控制寄存器CR2决定。

5.转换时间

转换时间由输入时钟和采样周期来决定。每个通道总的转换时间=TSampling+Tconversion Tsampling(采样时间)

转换时间=采样时间+12.5个周期

STM32的ADC最大的转换速率为1Mhz,也就是转换时间为1us(在ADCCLK=14M,采样周期为1.5个ADC时钟下得到)
不要让 ADC 的时钟超过 14M ,否则将导致结果准确度下降。

6.数据寄存器 

数据转换完成后的存放在2种数据寄存器中,分别是注入通道数据寄存器和规则通道数据寄存器。

注入通道数据寄存器

数据寄存器有4个,由于注入通道最多有4个,所以注入通道转换的数据都有固定的存放位置,不会跟规则寄存器那样产生数据覆盖的问题。

规则通道数据寄存器

负责存放规则通道转换的数据,通过32位寄存器ADC_DR来存放。

7.中断

规则和注入组转换结束时能产生中断,当模拟看门狗状态位被设置时也能产生中断。它们都有 独立的中断使能位。
数据转换完成之后可以产生中断,有三种情况,如下图所示:

二、实验步骤

1.配置NVIC

主要是配置ADC中断优先级,这边的对于优先级配置不懂的可以看之前的文章

STM32(三):外部中断 (标准库函数)_stm32外部中断的库函数程序怎么写-CSDN博客

static void ADC_NVIC_Config(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* 配置中断优先级 */  NVIC_InitStructure.NVIC_IRQChannel = ADCx_IRQ;/* 设置抢占式优先级为0 */NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;/* 设置子优先级为0 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;/* 使能外部中断通道 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/* 初始化配置嵌套向量中断控制器 */NVIC_Init(&NVIC_InitStructure);
}

2.GPIO 初始化

主要是对ADC IO口引脚的配置,同时还要使能端口时钟

static void ADCx_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;/* 打开 ADC IO端口时钟 */ADC_APBxClock_FUN(ADC_GPIO_CLK, ENABLE );	/* 配置 ADC IO 引脚模式 */GPIO_InitStructure.GPIO_Pin = ADC_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	/* 初始化 ADC IO */GPIO_Init(ADC_PORT, &GPIO_InitStructure);				
}

3.配置工作模式

static void ADCx_Mode_Config(void)
{ADC_InitTypeDef ADC_InitStructure;	/* 打开ADC时钟 */ADC_APBxClock_FUN(ADC_CLK,ENABLE );	/* ADC 模式配置 *//* 只使用一个ADC,属于单模式 */ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	/* 禁止扫描模式,多通道才要,单通道不需要 */ADC_InitStructure.ADC_ScanConvMode = DISABLE ; /* 连续转换模式 */ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;/* 不用外部触发转换,软件开启即可 */ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;/* 转换结果右对齐 */ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	/* 转换通道1个 */ADC_InitStructure.ADC_NbrOfChannel = 1;			/* 初始化ADC */ADC_Init(ADCx, &ADC_InitStructure);/* 配置ADC时钟为PCLK2的8分频,即9MHz */RCC_ADCCLKConfig(RCC_PCLK2_Div8);/* 配置 ADC 通道转换顺序为1,第一个转换,采样时间为55.5个时钟周期 */ADC_RegularChannelConfig(ADCx, ADC_CHANNEL, 1, ADC_SampleTime_55Cycles5);/* ADC 转换结束产生中断,在中断服务程序中读取转换值 */ADC_ITConfig(ADCx, ADC_IT_EOC, ENABLE);/* 开启ADC ,并开始转换 */ADC_Cmd(ADCx, ENABLE);/* 初始化ADC 校准寄存器   */ADC_ResetCalibration(ADCx);/*等待校准寄存器初始化完成 */while(ADC_GetResetCalibrationStatus(ADCx));	/* ADC开始校准*/ADC_StartCalibration(ADCx);/*等待校准完成 */while(ADC_GetCalibrationStatus(ADCx));/* 由于没有采用外部触发,所以使用软件触发ADC转换  */ADC_SoftwareStartConvCmd(ADCx, ENABLE);
}

记得配置ADC初始化函数,将上述配置放在一个函数ADCx_Init()

void ADCx_Init(void)
{ADCx_GPIO_Config();ADC_NVIC_Config();ADCx_Mode_Config();	
}

4.中断函数

但到这里我们的中断配置还没结束!!重点!!易踩坑!!

我们还需要在stm32f10x_it.c的文件里面加上新的中断服务函数 ADC_IRQHandler_FUN(),记得加上头文件,例如:

#include "bsp/adc/bsp_adc.h"__IO uint16_t ADC_ConvertedValue;void ADC_IRQHandler_FUN(void)
{if(ADC_GetITStatus(ADCx,ADC_IT_EOC)!=RESET){ADC_ClearITPendingBit(ADCx,ADC_IT_EOC);/* 读取ADC的转换值 */ADC_ConvertedValue = ADC_GetConversionValue(ADCx);}
}

5.串口发送函数

主要是讲电压数据发送到XCOM串口助手中,运用到printf()函数,这边可以看发过的这篇文章

STM32(九):USART串口通信 (标准库函数)-CSDN博客

三、实操代码

程序分为3个文件:bsp_adc.c、bsp_adc.h、main.c

1.bsp_adc.c 

/* 包含头文件 ----------------------------------------------------------------*/
#include "bsp/adc/bsp_adc.h"/*** 函数功能: ADC GPIO 初始化*/
static void ADCx_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;/* 打开 ADC IO端口时钟 */ADC_APBxClock_FUN(ADC_GPIO_CLK, ENABLE );	/* 配置 ADC IO 引脚模式 */GPIO_InitStructure.GPIO_Pin = ADC_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	/* 初始化 ADC IO */GPIO_Init(ADC_PORT, &GPIO_InitStructure);				
}/*** 函数功能: 配置ADC工作模式*/
static void ADCx_Mode_Config(void)
{ADC_InitTypeDef ADC_InitStructure;	/* 打开ADC时钟 */ADC_APBxClock_FUN(ADC_CLK,ENABLE );	/* ADC 模式配置 *//* 只使用一个ADC,属于单模式 */ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	/* 禁止扫描模式,多通道才要,单通道不需要 */ADC_InitStructure.ADC_ScanConvMode = DISABLE ; /* 连续转换模式 */ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;/* 不用外部触发转换,软件开启即可 */ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;/* 转换结果右对齐 */ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	/* 转换通道1个 */ADC_InitStructure.ADC_NbrOfChannel = 1;			/* 初始化ADC */ADC_Init(ADCx, &ADC_InitStructure);/* 配置ADC时钟为PCLK2的8分频,即9MHz */RCC_ADCCLKConfig(RCC_PCLK2_Div8);/* 配置 ADC 通道转换顺序为1,第一个转换,采样时间为55.5个时钟周期 */ADC_RegularChannelConfig(ADCx, ADC_CHANNEL, 1, ADC_SampleTime_55Cycles5);/* ADC 转换结束产生中断,在中断服务程序中读取转换值 */ADC_ITConfig(ADCx, ADC_IT_EOC, ENABLE);/* 开启ADC ,并开始转换 */ADC_Cmd(ADCx, ENABLE);/* 初始化ADC 校准寄存器   */ADC_ResetCalibration(ADCx);/*等待校准寄存器初始化完成 */while(ADC_GetResetCalibrationStatus(ADCx));	/* ADC开始校准*/ADC_StartCalibration(ADCx);/*等待校准完成 */while(ADC_GetCalibrationStatus(ADCx));/* 由于没有采用外部触发,所以使用软件触发ADC转换  */ADC_SoftwareStartConvCmd(ADCx, ENABLE);
}/*** 函数功能: NVIC配置:ADC中断优先级配置*/
static void ADC_NVIC_Config(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* 配置中断优先级 */  NVIC_InitStructure.NVIC_IRQChannel = ADCx_IRQ;/* 设置抢占式优先级为0 */NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;/* 设置子优先级为0 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;/* 使能外部中断通道 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/* 初始化配置嵌套向量中断控制器 */NVIC_Init(&NVIC_InitStructure);
}/*** 函数功能: ADC初始化*/
void ADCx_Init(void)
{ADCx_GPIO_Config();ADC_NVIC_Config();ADCx_Mode_Config();	
}

2.bsp_adc.h

#ifndef __ADC_H__
#define	__ADC_H__/* 包含头文件 ----------------------------------------------------------------*/
#include <stm32f10x.h>// 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响/********************ADC输入通道(引脚)配置**************************/
#define    ADC_APBxClock_FUN             RCC_APB2PeriphClockCmd#define    ADCx                          ADC3
#define    ADC_CLK                       RCC_APB2Periph_ADC3
#define    ADCx_IRQ                      ADC3_IRQn
#define    ADC_IRQHandler_FUN            ADC3_IRQHandler#define    ADC_GPIO_CLK                  RCC_APB2Periph_GPIOC  
#define    ADC_PORT                      GPIOC#define    ADC_PIN                       GPIO_Pin_0        // 连接至板载精密可调电阻(需加跳帽)
#define    ADC_CHANNEL                   ADC_Channel_10    // 连接至板载精密可调电阻(需加跳帽)
//#define    ADC_PIN                       GPIO_Pin_1        // 连接至板载光敏电阻(需加跳帽)
//#define    ADC_CHANNEL                   ADC_Channel_11    // 连接至板载光敏电阻(需加跳帽)/* 扩展变量 ------------------------------------------------------------------*/
/* 函数声明 ------------------------------------------------------------------*/
void ADCx_Init(void);#endif /* __ADC_H__ */

3.main.c

/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f10x.h"#include "stm32f10x.h"
#include "bsp/led/bsp_led.h"
#include "bsp/key/bsp_key.h"
#include "bsp/delay/delay.h"
#include "bsp/systick/bsp_SysTick.h"
#include "bsp/GeneralTIM/bsp_GeneralTIM.h" 
#include "bsp/adc/bsp_adc.h"/* 用于保存转换计算后的电压值 */	 
float ADC_ConvertedValueLocal;   /* 扩展变量 ------------------------------------------------------------------*/
extern __IO uint16_t ADC_ConvertedValue;
/* 私有函数原形 --------------------------------------------------------------*/
static void Delay(uint32_t time);/*** 函数功能: 主函数.*/
int main(void)
{  /* 调试串口初始化配置,115200-N-8-1.使能串口发送和接受 */USARTx_Init(); /* ADC 初始化 */ADCx_Init();/* 调用格式化输出函数打印输出数据 */printf("----这是一个ADC单通道电压采集实验-----\n"); /* 无限循环 */while (1){ ADC_ConvertedValueLocal =(float)ADC_ConvertedValue*3.3/4096; printf("AD转换原始值 = 0x%04X \r\n", ADC_ConvertedValue); printf("计算得出电压值 = %f V \r\n",ADC_ConvertedValueLocal); Delay(1000);}
}

四、实验效果

ADC电压检测

参考博客:

STM32—ADC详解_stm32最好可设置几个模拟通道-CSDN博客

结束语

本文以STM32VET6为例讲解了如何用STM32单片机中ADC进行电位检测,并发送到XCOM串口中显示,并指出其中的易坑点。希望对大家有所帮助!如果还有什么问题,欢迎评论区留言,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/23182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

arcpy批量导出图且图名为shp属性值

1.打开arcmap加载需要导出的图。需求是逐村显示“村界内图斑”并导出为图&#xff0c;在导出每个村时不显示周围的村和“村界内图斑” 2.arcmap上方空白处右键打开“数据驱动页面” 3.在“数据驱动页面”工具条点击第一个图标&#xff0c;打开“设置数据驱动页面” 4.在“设置…

Oracle触发器的用途

0.DDL和DML DDL:data manipulation language,数据操作语言&#xff0c;主要命令有select&#xff0c;update&#xff0c;insert,delete,主要用于对数据库中的数据进行操作 DDL:data definition language,数据定义语言&#xff0c;主要命令有create&#xff0c;alter&#xff…

Java1.8全套家政上门服务+springboot+ mysql +Thymeleaf 技术架构开发,家政APP系统在线派单,师傅入驻全套商业源码

Java1.8全套家政上门服务springboot mysql Thymeleaf 技术架构开发&#xff0c;家政APP系统在线派单&#xff0c;师傅入驻全套商业源码 家政预约上门小程序的实用性&#xff1f; 家政预约上门小程序的实用性主要体现在以下几个方面&#xff1a; 一、方便快捷的预约体验&#…

尚硅谷2024新版3小时速通Docker教程

尚硅谷2024新版3小时速通Docker教程 百度网盘&#xff1a;https://pan.baidu.com/s/1SncgHbdJehvZspjcrrbLSw?pwd6c27

【数据结构与算法 经典例题】随机链表的复制(图文详解)

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法 经典例题》C语言 期待您的关注 ​ 目录 一、问题描述 二、解题思路 三、代码实现 1. 原链表中节点的数据拷…

采用JWT令牌和Filter进行登录拦截认证

原理描述&#xff1a; 1、第一次登录的时候&#xff0c;生成JWT令牌&#xff0c;并JWT令牌存放在localStorage。 localStorage.setItem(token, token); 2、每次通过axios发送请求的时候&#xff0c;都将这个令牌获取&#xff0c;并放于header中发送。 也就是JWT令牌只在登录…

PCL 抛物线回归拟合(Quadratic,二维)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 这里仍然是最小二乘法的应用,其推导过程如下所述: 1.二次函数模型: 其中,a、b 和 c 是需要确定的参数。 2.最小二乘法 假设我们有一组数据点 ( x 1 ​ , y

实用软件分享---中医自学宝典(安卓)

专栏介绍:本专栏主要分享一些实用的软件(Po Jie版); 声明1:软件不保证时效性;只能保证在写本文时,该软件是可用的;不保证后续时间该软件能一直正常运行;不保证没有bug;如果软件不可用了,我知道后会第一时间在题目上注明(已失效)。介意者请勿订阅。 声明2:本专栏的…

usb设备在主机和VMWare虚拟机中切换连接

操作&#xff1a;点击菜单栏虚拟机(M)>可移动设备>选择自己的usb设备>连接(断开与 主机 的连接)

【Vue】路由介绍

一、引入 思考 单页面应用程序&#xff0c;之所以开发效率高&#xff0c;性能好&#xff0c;用户体验好 最大的原因就是&#xff1a;页面按需更新 比如当点击【发现音乐】和【关注】时&#xff0c;只是更新下面部分内容&#xff0c;对于头部是不更新的 要按需更新&#xff…

上海亚商投顾:沪指震荡下跌 两市成交不足7000亿元

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日震荡调整&#xff0c;创业板指冲高回落。车路云概念股持续活跃&#xff0c;万通智控、鸿泉物联、华体…

【Vue】异步更新 $nextTick

文章目录 一、引出问题二、解决方案三、代码实现 一、引出问题 需求 编辑标题, 编辑框自动聚焦 点击编辑&#xff0c;显示编辑框让编辑框&#xff0c;立刻获取焦点 即下图上面结构隐藏&#xff0c;下面结构显示&#xff0c;并且显示的时候让它自动聚焦。 代码如下 问题 “…

【Multi-Feature FAS】《Face Anti-Spoofing Based on Multi-Feature Fusion》

文章目录 原文贡献 / 相关工作作者的方法评价 原文 [1]杨敏.基于多特征融合的人脸防伪技术研究[D].武汉大学,2019. 贡献 / 相关工作 针对攻击对象存在背景依赖和非刚性运动造成的深度信息缺失问题&#xff0c;采用边缘信息放大差异 各种 loss 数据库 评价指标 本节将会对…

Java——Stream流(2/2):Stream流的中间方法、终结方法(方法、案例演示)

目录 Stream流的中间方法 方法 案例演示 Stream流的终结方法 方法 案例演示1 收集 案例演示2 Stream流的中间方法 方法 中间方法指的是调用完成后会返回新的Stream流&#xff0c;可以继续使用(支持链式编程)。 Stream提供的常用中间方法说明Stream<T> filter(P…

gkuubibiih

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

vue2的element的table组件使用form校验

1.需求描述 vue2有时候做自增表格el-table&#xff0c;希望能够带一些校验&#xff0c;但又不想手搓校验逻辑&#xff0c;可以借用el-form的校验逻辑。 2.代码处理 1. html <template><div class"sad-cont"><el-form ref"form" :model&…

LabVIEW传感器虚拟综合实验系统

LabVIEW传感器虚拟综合实验系统 开发了一个基于LabVIEW的传感器虚拟综合实验系统&#xff0c;该系统集成了NIELVIS和CSY系列传感器实验平台&#xff0c;通过图形化编程语言进行数据处理和实验管理。系统允许用户进行多种传感器参数的测量和实验报告的自动生成&#xff0c;支持…

不装了,我是知识星球的星主,我摊牌了~

作者&#xff1a;哈哥撩编程 &#xff08;视频号同名&#xff09; 图书作者&#xff1a;程序员职场效能宝典 博客专家&#xff1a;全国博客之星第四名 超级个体&#xff1a;COC上海社区主理人 特约讲师&#xff1a;谷歌亚马逊分享嘉宾 科技博主&#xff1a;极星会首批签约…

关系代数与规范化

本文是根据自己的理解&#xff0c;结合实践整理所得&#xff0c;有兴趣的可以参考学习。

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于麻雀搜寻优化算法的代理购电用户用电量多维度协同校核》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…