找回密码
 立即注册
楼主: 梁工

实际精度13位, 真12位ADC过采样到16位分辨率,完整方案

 火 [复制链接]

83

主题

8318

回帖

1万

积分

超级版主

积分
17938
 楼主| 发表于 2025-12-20 11:17:27 | 显示全部楼层
代码*** 发表于 2025-12-20 00:45
对于单路的,集成方案最方便,一个芯片解决问题。
我在做的是16通道的,还在考虑用哪种方案。
第一种成本 ...

你这是第一次做PT100吗?
回复

使用道具 举报 送花

已绑定手机

1

主题

63

回帖

4152

积分

论坛元老

积分
4152
发表于 2025-12-21 11:16:39 | 显示全部楼层
及时*** 发表于 2024-8-9 15:55
梁工,您好!

想跟您请教一个问题:

您好,能一下完整的电路图吗?谢谢
回复

使用道具 举报 送花

19

主题

55

回帖

567

积分

高级会员

积分
567
发表于 2025-12-21 14:01:42 | 显示全部楼层
梁*** 发表于 2025-12-20 11:17
你这是第一次做PT100吗?

10多年前做过单路的,随便整了个1ma恒流源和ADC,485通信就完事了。这次要求高,多路,24位的
回复

使用道具 举报 送花

1

主题

4

回帖

51

积分

注册会员

积分
51
发表于 2026-1-22 15:38:43 | 显示全部楼层
测量两个信号,是不是需要两路独立的方波生成三角波,

点评

如果测量的是缓慢的直流电压,则一个三角波就可以了,用电容耦合到各路ADC输入端。 如果测量的是交流电压,则有两个方案: 1、每一路ADC都要独立三角波电路,以免通道影响。 2、一路三角波,经过运放缓  详情 回复 发表于 2026-1-22 18:24
回复

使用道具 举报 送花

83

主题

8318

回帖

1万

积分

超级版主

积分
17938
 楼主| 发表于 2026-1-22 18:24:40 | 显示全部楼层
stc*** 发表于 2026-1-22 15:38
测量两个信号,是不是需要两路独立的方波生成三角波,

如果测量的是缓慢的直流电压,则一个三角波就可以了,用电容耦合到各路ADC输入端。
如果测量的是交流电压,则有两个方案:
      1、每一路ADC都要独立三角波电路,以免通道影响。
      2、一路三角波,经过运放缓冲(低阻抗)输出,再用电容耦合到各路ADC输入端。
回复

使用道具 举报 送花

已绑定手机

2

主题

359

回帖

1436

积分

金牌会员

积分
1436
发表于 2026-2-10 04:27:58 | 显示全部楼层
学习了,谢谢分享
回复

使用道具 举报 送花

16

主题

156

回帖

953

积分

高级会员

积分
953
发表于 2026-5-28 09:41:58 | 显示全部楼层

#include        "config.h"
#include        "UART1.h"


/*************        功能说明        **************

用户请先别修改程序, 直接下载"ADC.hex"测试.

下载时选择主频24MHZ. 测试时, 电脑的串口助手设置115200,8,n,1.

本程序演示使用开天斧三.1的P1.5做12位ADC输入过采样得到16位ADC结果, 通过串口1(P3.1)发送给上位机,波特率115200,8,n,1.

打开串口助手开始接收, 再打开"STC调试助手"中的"7段数码管"即可看到结果.


******************************************/


/*************        本地常量声明        **************/

#define        ADC_CHN                5        /* 定义ADC通道 */



/*************        IO口定义        **************/

/*************        本地变量声明        **************/

u16        adc;                        // 当前完成的ADC值, 用于过程计算, 不传递结果数值, 用户层不可见.
u32        adc_sum;                // ADC累加和, 中断使用的变量, 用户层不可见
bit        B_ADC_OK;                // ADC已测量计算OK.
u16        adc_cnt;                // ADC已转换次数, 中断使用, 用户层不可见.

u16        adc_16bit;                // 测量的16位ADC值
u8        trian_cnt;


/*************        本地函数声明        **************/
void        ADC_config(void);
u8                Timer0_Config(u8 t, u32 reload);        //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us)

/****************  外部函数声明和外部变量声明 *****************/



/**********************************************/

void main(void)
{
        u16        j;

        P0M1 = 0;        P0M0 = 0;        //设置为准双向口
        P1M1 = 0;        P1M0 = 0;        //设置为准双向口
        P2M1 = 0;        P2M0 = 0;        //设置为准双向口
        P3M1 = 0;        P3M0 = 0;        //设置为准双向口
        P5M1 = 0;        P5M0 = 0;        //设置为准双向口

        ADC_config();        //初始化ADC
        Timer0_Config(0, MAIN_Fosc / 12800);        //t=0: reload值是主时钟周期数,  (中断频率)
        P1n_push_pull(Pin1);        // P1.1推挽输出方波经过RC转三角波
       
        EA = 1;
        UART1_config(115200UL, 1, 0);        // brt: 通信波特率,  timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1,  =1: 切换到P3.6 P3.7,  =2: 切换到P1.6 P1.7.

        while (1)
        {

                if(B_ADC_OK)        //ADC转换完成
                {
                        B_ADC_OK = 0;

                        UART1_PrintString("7SEGS");                //7段数码管命令头
                        TX1_write2buff(0x00);
                        TX1_write2buff(0x00);
                        TX1_write2buff(0x00);

                        j = adc_16bit;
                //        j = (u16)((float)j*1.001f);        //增益校正, 根据实际情况使用
                        TX1_write2buff( j/10000 + '0');
                        TX1_write2buff((j%10000)/1000 + '0');
                        TX1_write2buff((j%1000)/100 + '0');
                        TX1_write2buff((j%100)/10 + '0');
                        TX1_write2buff( j%10 + '0');
                        TX1_write2buff(0x0);
                }
               
        }
}
/**********************************************/


//========================================================================
// 函数: void        ADC_config(void)
// 描述: ADC配置函数.
// 参数: None
// 返回: none.
// 版本: V1.0, 2021-12-23
//========================================================================
#define ADC_POWER        (1<<7)        /* 打开ADC电源 */
#define ADC_START        (1<<6)        /* 自动清0 */
#define ADC_FLAG        (1<<5)        /* 软件清0 */
#define ADC_EPWMT        (1<<4)        /* 允许PWM触发ADC */

#define        RES_FMT                (1<<5)        /* ADC结果格式 0: 左对齐, ADC_RES: D9 D8 D7 D6 D5 D4 D3 D2, ADC_RESL: D1 D0 0  0  0  0  0  0 */
                                                        /*             1: 右对齐, ADC_RES: 0  0  0  0  0  0  D9 D8, ADC_RESL: D7 D6 D5 D4 D3 D2 D1 D0 */
#define        ADC_SPEED        3                /* 0~15, ADC时钟 = SYSclk/2/(n+1) */

#define CSSETUP                (1<<7)        /* 0~1,  ADC通道选择时间      0: 1个ADC时钟, 1: 2个ADC时钟,  默认0(默认1个ADC时钟)        */
#define CSHOLD                (1<<5)        /* 0~3,  ADC通道选择保持时间  (n+1)个ADC时钟, 默认1(默认2个ADC时钟)                                        */
#define SMPDUTY                20                /* 10~31, ADC模拟信号采样时间  (n+1)个ADC时钟, 默认10(默认11个ADC时钟)                                */
                                                        /* ADC转换时间: 10位ADC固定为10个ADC时钟, 12位ADC固定为12个ADC时钟.                                 */
void        ADC_config(void)
{
        P_SW2 |= 0x80;        //访问XSFR
        P1n_pure_input((1<<ADC_CHN));        //设置要做ADC的IO做高阻输入
        ADC_CONTR = ADC_POWER | ADC_EPWMT | 0;        //ADC on + channel
        ADCCFG    = RES_FMT + ADC_SPEED;
        ADCTIM = CSSETUP + CSHOLD + SMPDUTY;
        EADC = 1;                        //允许ADC中断
}

//========================================================================
// 函数: void ADC_ISR (void) interrupt ADC_VECTOR
// 描述: ADC中断函数.
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2018-4-1
// 备注:
//========================================================================
void ADC_ISR (void) interrupt ADC_VECTOR
{
        ADC_CONTR &= ~ADC_FLAG;                //清除中断标志

        adc = (u16)ADC_RES * 256 + (u16)ADC_RESL;        //读ADC结果
        adc_sum += adc;                //adc累加和

        if(++trian_cnt == 8)        //产生方波, 经过RC转成三角波
        {
                trian_cnt = 0;
                P11 = ~P11;
        }
       
//        if(++adc_cnt == 256)        // 累加256次完成,  时间20ms
        if(++adc_cnt == 2048)        // 累加2048次完成, 时间160ms
        {
                P11 = 0;
                adc_cnt = 0;
                trian_cnt = 0;
                B_ADC_OK = 1;        //标志已完成一次过采样, 20ms

        //        adc_16bit = adc_sum/16;                //ADC结果
                adc_16bit = adc_sum/128;        //ADC结果
                adc_sum = 0;
        }
}

//========================================================================
// 函数: void timer0_int (void) interrupt TIMER0_VECTOR
// 描述:  timer0中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2018-12-20
//========================================================================
void timer0_ISR (void) interrupt TIMER0_VECTOR
{
        ADC_RES  = 0;
        ADC_RESL = 0;
        ADC_CONTR = 0x80 | ADC_START | ADC_CHN;        //启动ADC
}



//========================================================================
// 函数:u8        Timer0_Config(u8 t, u32 reload)
// 描述: timer0初始化函数.
// 参数:      t: 重装值类型, 0表示重装的是系统时钟数, 其余值表示重装的是时间(us).
//       reload: 重装值.
// 返回: 0: 初始化正确, 1: 重装值过大, 初始化错误.
// 版本: V1.0, 2018-12-20
//========================================================================
u8        Timer0_Config(u8 t, u32 reload)        //t=0: reload值是主时钟周期数,  t=1: reload值是时间(单位us)
{
        TR0 = 0;        //停止计数

        if(t != 0)        reload = (u32)(((float)MAIN_Fosc * (float)reload)/1000000UL);        //重装的是时间(us), 计算所需要的系统时钟数.
        if(reload >= (65536UL * 12))        return 1;        //值过大, 返回错误
        if(reload < 65536UL)        AUXR |= 0x80;                //1T mode
        else
        {
                AUXR &= ~0x80;        //12T mode
                reload = reload / 12;
        }
        reload = 65536UL - reload;
        TH0 = (u8)(reload >> 8);
        TL0 = (u8)(reload);

        ET0 = 1;        //允许中断
        PT0 = 1;        //高优先级中断
        TMOD = (TMOD & ~0x03) | 0;        //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
        TR0 = 1;        //开始运行
        return 0;
}

回复

使用道具 举报 送花

已绑定手机

5

主题

34

回帖

341

积分

中级会员

积分
341
发表于 2026-7-15 08:56:25 | 显示全部楼层
梁工,你好,请教一下,场景是测量温度,我完成256次累加,对结果右移6位,获得14bit量化结果,因为我看您视频中说stc8的adc实际精度能到14bit,就不需要用16bit量化了吧,这样理解对吗?
截图202607150853483070.jpg

点评

按需要的位数来抽取,256次累加则位数增加8位,等效为256倍过采样,ADC为12位则累加结果为20位。 需要14位结果,则右移6位即可,等效为4倍过采样。 ADC的硬件精度是12.5~13位,14位为分辨率。 要多少位量化结果由项  详情 回复 发表于 2026-7-15 09:37
回复

使用道具 举报 送花

83

主题

8318

回帖

1万

积分

超级版主

积分
17938
 楼主| 发表于 2026-7-15 09:37:52 | 显示全部楼层
ckhz*** 发表于 2026-7-15 08:56
梁工,你好,请教一下,场景是测量温度,我完成256次累加,对结果右移6位,获得14bit量化结果,因为我看您 ...

按需要的位数来抽取,256次累加则位数增加8位,等效为256倍过采样,ADC为12位则累加结果为20位。
需要14位结果,则右移6位即可,等效为4倍过采样。
ADC的硬件精度是12.5~13位,14位为分辨率。
要多少位量化结果由项目要求而定,但不要超过16位。

测量温度要根据传感器特性来确定位宽。不知道你的是什么传感器?
回复

使用道具 举报 送花

已绑定手机

5

主题

34

回帖

341

积分

中级会员

积分
341
发表于 2026-7-15 12:58:31 | 显示全部楼层
梁*** 发表于 2026-7-15 09:37
按需要的位数来抽取,256次累加则位数增加8位,等效为256倍过采样,ADC为12位则累加结果为20位。
需要14位 ...

想用pt100和k型采集温度,都使用过采样的方案,14位分辨率好还是16位分辨率好

点评

看你的调理电路和要求的温度分辨率,才能确定。  详情 回复 发表于 2026-7-15 14:59
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-11-10 10:24 , Processed in 0.086532 second(s), 108 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表