找回密码
 立即注册
查看: 882|回复: 12

STC32驱动TM7707的24位ΣADC开源教程

[复制链接]
  • TA的每日心情
    开心
    15 小时前
  • 签到天数: 90 天

    [LV.6]常住居民II

    38

    主题

    986

    回帖

    6771

    积分

    荣誉版主

    冲哥视频教程和各种开源资料QQ交流群884047237,可群

    积分
    6771
    QQ
    发表于 2023-11-10 16:07:15 | 显示全部楼层 |阅读模式
    本帖最后由 电子DIY小家 于 2023-11-10 16:15 编辑

    开题之前先给大家看一段花絮,背景是我在一家店买了个TM7707的模块,再调驱动的时候ADC数值一直和我的理论值有极大地偏差,最后一点点定位到了错误的地方,发现就是店家的示例代码的问题,然后我去找店家据理力争:(图1是店家自信的回复,图2是店家提供的示例代码,图3是24位adc的三个8位数据按照店家代码的合并过程)

    截图202311101350275833.jpg     截图202311101350482538.jpg     截图202311101350594720.jpg

    当然,最后店家也意识到了问题,已经及时把示例代码改正了!(说实话我还是比较喜欢这里这个老板这里桀骜不驯,迷之自信的态度的哈哈~

    有人可能说为什么单片机内部明明有有ADC了,这里还要外挂一个ADC芯片呢?
    因为STC内部虽然有一个12位真有效值的ADC,且可以用过采样的办法实现更高的位数,但是对于一些微小的电压信号就不太好检测,其次有些场合他对ADC分辨率有硬性的要求(例如一定检测范围内的分辨率按照实际的位数换算下来有14位,然而器件本身只要12位的ADC,认证机构的老师就不会认可你做这个方案),所以对ADC就有了自带PGA和高精度的要求,所以这里外挂一个ADC是一个比较好的办法,当然方法不唯一。

    截图202311101449258671.jpg

    这里首先来看下他天花乱坠的介绍,首先我们用的是TM7707,和TM7708只有少了一个通道的区别,其次他支持5V和3.3V供电,这点比较人性化,其次TM7707有两个差分通道,24位的精度,1~128倍的PGA增益,看下来绝对满足很多日常使用了。


    先来看下这个模块(这里用的是淘宝店铺名为 “渡河蚂蚁电子工作室” 的模块)和模块的原理图:


    截图202311101452241532.jpg


    截图202311101454162082.jpg

    当然这个原理图比较简单,可以自己对照手册去看下每个引脚的含义,这里就不多介绍了,只有特别注意的就是这个基准电压源是2.5V的,也就是这个ADC的测量范围就是0-2.5V。

    1.    模块            单片机
    2. TM7707_CS     <>    P4^6;        
    3. TM7707_RESET  <>    P4^5;        
    4. TM7707_DIN    <>    P4^0;        
    5. TM7707_SCK    <>    P4^3;        
    6. TM7707_DOUT   <>    P4^1;        
    7. TM7707_DRDY    <>    P4^7;        
    复制代码
    接线方式如上所示~用的STC32G单片机内部的硬件SPI,自己移植到自己项目的时候一定要注意引脚,如果移植到了没有硬件SPI的引脚的话要把代码改为软件模拟SPI的方式驱动!!


    硬件解决了,那么下面来看软件,这个模块非常的简单,只有很少的几个寄存器,这里直接一个个看下来了:

    1.通信寄存器:(所有寄存器的选择都是通过这个寄存器为枢纽来选择的~放在早些年这就算是个哨兵!)

    截图202311101505172625.jpg

    可以看到最高位b7是忙碌标志位,b6-b4是寄存器选择位,b3是读写位,b2等待模式模,b1-b0是通道选择位,这里特别要注意的就是通道选择位:

    截图202311101511599781.jpg

    基于这个通道选择位,我们可以直接在代码里增加如下的宏定义来给我选择,这样可以保证寄存器出错的概率降到最低:

    1. #define REG_COMM      0X00                                                                                 // 通信寄存器
    2. #define REG_SETUP     0X10                                                                                 // 设置寄存器
    3. #define REG_CLOCK     0X20                                                                                 // 时钟寄存器
    4. #define REG_DATA      0X30                                                                                 // 数据寄存器
    5. #define REG_ZERO_CH1  0X60                                                                                 // CH1 偏移寄存器
    6. #define REG_FULL_CH1  0X70                                                                                 // CH1 满量程寄存器
    7. #define REG_ZERO_CH2  0X61                                                                                 // CH2 偏移寄存器
    8. #define REG_FULL_CH2  0X71                                                                                 // CH1 满量程寄存器
    复制代码
    相应的读写位和通道选择位也可以用宏定义来选择:

    1. #define WRITE         0X00                                                                                // 写操作
    2. #define READ          0X08                                                                                // 读操作
    3. #define CH_0          0                                                                                         // 通道0
    4. #define CH_1          1                                                                                         // 通道1
    复制代码


    2.设置寄存器(主要是校准模式、增益选择、极性选择)

    截图202311101516503286.jpg

    这里先来看模式:
    截图202311101528288837.jpg
    这里的四种模式就可以用宏定义写成这样的格式:
    1. #define MD_NORMAL          (0 << 6)                                                                        // 正常模式
    2. #define        MD_CAL_SELF          (1 << 6)                                                                        // 自校准模式
    3. #define        MD_CAL_ZERO          (2 << 6)                                                                        // 校准0刻度模式
    4. #define        MD_CAL_FULL          (3 << 6)                                                                        // 校准满刻度模式
    复制代码



    相对应的增益也可以看下手册的介绍:


    截图202311101530153595.jpg

    增益的宏定义就可以这么写:
    1. #define        GAIN_1                  (0 << 3)                                                                        // 增益
    2. #define        GAIN_2                  (1 << 3)                                                                        // 增益
    3. #define        GAIN_4                  (2 << 3)                                                                        // 增益
    4. #define        GAIN_8                  (3 << 3)                                                                        // 增益
    5. #define        GAIN_16                  (4 << 3)                                                                        // 增益
    6. #define        GAIN_32                  (5 << 3)                                                                        // 增益
    7. #define        GAIN_64                  (6 << 3)                                                                        // 增益
    8. #define        GAIN_128          (7 << 3)                                                                        // 增益
    复制代码




    极性和缓冲器和滤波器的定义如下图所示:


    截图202311101532428216.jpg


    这里的宏定义就可以这么写:
    1. #define        BIPOLAR                  (0 << 2)                                                                        // 双极性输入 ,无论双极性还是单极性都不改变任何输入信号的状态,它只改变输出数据的代码和转换函数上的校准点
    2. #define        UNIPOLAR          (1 << 2)                                                                        // 单极性输入
    3.                                                                                                                                
    4. #define        BUF_NO                  (0 << 1)                                                                        // 输入无缓冲(内部缓冲器不启用)
    5. #define        BUF_EN                  (1 << 1)                                                                        // 输入有缓冲 (启用内部缓冲器)
    6.                                                                        
    7. #define        FSYNC_0                   0                                                                            // 启用滤波器同步
    8. #define        FSYNC_1                   1                                                                            // 不启用滤波器同步
    复制代码




    3.数据寄存器(24位的数据可以从这里读出来,只读寄存器)


    截图202311101534399506.jpg
    当然这个寄存器的数据就是高位在前,低位在后,一共三个字节的数据。


    相对重要的几个寄存器就是上面的这些,当然其实还有几个寄存器,如果需要的话也可以去看看。


    截图202311101550358752.jpg


    经过上面的梳理,其实初始化就变的特别简单了
    1. //========================================================================
    2. // 函数名称: TM7707_InitADC
    3. // 函数功能: 配置SPI接口,用于连接 TM7707
    4. // 入口参数: 无
    5. // 函数返回: 无
    6. // 其他说明:
    7. //========================================================================
    8. void TM7707_InitADC(void)
    9. {
    10.         SPI_Init1();
    11.        
    12.         TM7707_CS = 0;
    13.         TM7707_Delay_ms(10);                                                                                                         // 延时10MS
    14.         TM7707_RST();                                                                                                                         // 硬件复位
    15.                                                                                                
    16.                                                                                                                
    17.                                                                                                
    18.         TM7707_Delay_ms(5);                                                                                                             // 延时10MS 在接口序列丢失的情况下,如果在DIN 高电平的写操作持续了足够长的时间(至少 32个串行时钟周期),TM7707 将会回到默认状态
    19.         TM7707_SyncSPI();                                                                                                                 // 同步TM7707 SPI时序
    20.         TM7707_Delay_ms(5);                                                                                                             // 延时10MS
    21.         TM7707_WriteByte(REG_CLOCK | WRITE | CH_0);                                                                // 先写通信寄存器,下一步是写时钟寄存器
    22.         TM7707_WriteByte(CLKDIS_0 | CLK_4_9152M | FS_50HZ);                                                // 4.9152MHZ晶振 刷新速率50Hz
    23.        
    24.         TM7707_CalibSelf(0);                                                                                                         // 内部自校准 CH0  每次上电进行一次自校准
    25.         TM7707_Delay_ms(10);                                                                                                         // 延时10MS
    26.         TM7707_CalibSelf(1);                                                                                                         // 内部自校准 CH0
    27.         TM7707_Delay_ms(10);                                                                                                         // 延时10MS
    28. }
    复制代码



    1.先打开片选并复位,主要就是rst引脚拉低一会儿即可
    2.同步时序,也就是直接发5个0XFF给模块
    3.选择时钟和刷新率
    4.自校准


    注意这里的校准用的是内部的校准~


    截图202311101556067235.jpg

    校准方法如上图所示,看上面文字一大堆,其实代码就非常简单:
    1. //========================================================================
    2. // 函数名称: TM7707_CalibSelf
    3. // 函数功能: 启动自校准. 内部自动短接AIN+ AIN-校准0位,内部短接到Vref 校准满位。此函数执行过程较长,实测约 180ms
    4. // 入口参数:  @ch:ADC通道,1或2
    5. // 函数返回: 无
    6. // 其他说明:
    7. //========================================================================
    8. void TM7707_CalibSelf(u8 ch)
    9. {
    10.         if(ch == 0)                                                                                                                                // 选择通道0
    11.         {                                                                                                                                                  // 自校准CH0
    12.                 TM7707_WriteByte(REG_SETUP | WRITE | CH_0);                                                        // 写通信寄存器,下一步是写设置寄存器,通道0
    13.                 TM7707_WriteByte(MD_CAL_SELF | CH1_GAIN_BIPOLAR_BUF | FSYNC_0);                // 启动自校准
    14.                 TM7707_WaitDRDY();                                                                                                        // 等待内部操作完成  --- 时间较长,约180ms
    15.         }
    16.         else if(ch == 1)
    17.         {                                                                                                                                                // 自校准CH1
    18.                 TM7707_WriteByte(REG_SETUP | WRITE | CH_1);                                                        // 写通信寄存器,下一步是写设置寄存器,通道0
    19.                 TM7707_WriteByte(MD_CAL_SELF | CH2_GAIN_BIPOLAR_BUF | FSYNC_0);                // 启动自校准
    20.                 TM7707_WaitDRDY();                                                                                                         // 等待内部操作完成  --- 时间较长,约180ms
    21.         }
    22. }
    复制代码


    直接选择设置寄存器,在按正常采集的配置设置一下,开启校准就可以了。


    上面的都是初始化的配置,然后是正常的adc采集:
    1. //========================================================================
    2. // 函数名称: TM7707_ReadADC
    3. // 函数功能: 读取ADC指定通道的数据
    4. // 入口参数: 无
    5. // 函数返回: 返回的24位adc数据
    6. // 其他说明:
    7. //========================================================================
    8. u32 TM7707_ReadADC(u8 ch)
    9. {
    10.         u32 ADC_V;
    11.         u8 i,a,b,c;
    12.         for(i = 0;i < 2;i++)                                                                                                        // 为了避免通道切换造成读数失效,读2次 */
    13.         {
    14.                 TM7707_WaitDRDY();                                                                                                        // 等待DRDY口线为0
    15.                 if(ch == 0)
    16.                 {
    17.                         TM7707_WriteByte(0x38);       
    18.                 }
    19.                 else if(ch == 1)
    20.                 {
    21.                         TM7707_WriteByte(0x39);
    22.                 }
    23.                 a = TM7707_Read1Byte();
    24.                 b = TM7707_Read1Byte();
    25.                 c = TM7707_Read1Byte();
    26.                 ADC_V = a;
    27.                 ADC_V = ADC_V<<8|b;
    28.                 ADC_V = ADC_V<<8|c;
    29.         }
    30.        
    31.         return ADC_V;
    32. }
    复制代码
    代码如上,其实就是读取那个寄存器的三个字节的数据,回到开头的问题,其实读取的三个字节分别是


    截图202311101600548290.jpg

    而不是像他那样的中间跳过了八个位的!


    最后看到主函数:
    1. //========================================================================
    2. // 函数名称: main
    3. // 函数功能: 主函数
    4. // 入口参数: 无
    5. // 函数返回: 无
    6. // 其他说明:
    7. //========================================================================
    8. void main(void)
    9. {
    10.         u32 ADCVAL;
    11.        
    12.     WTST = 0;                                                                                                          // 设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    13.     EAXFR = 1;                                                                                                         // 扩展寄存器(XFR)访问使能
    14.     CKCON = 0;                                                                                                         // 提高访问XRAM速度
    15.                                                                                                                                   
    16.     P0M1 = 0x00;   P0M0 = 0x00;                                                                   // 设置为准双向口
    17.     P1M1 = 0x00;   P1M0 = 0x00;                                                                   // 设置为准双向口
    18.     P2M1 = 0x00;   P2M0 = 0x00;                                                                   // 设置为准双向口
    19.     P3M1 = 0x00;   P3M0 = 0x00;                                                                   // 设置为准双向口
    20.     P4M1 = 0x00;   P4M0 = 0x00;                                                                   // 设置为准双向口
    21.     P5M1 = 0x00;   P5M0 = 0x00;                                                                   // 设置为准双向口
    22.     P6M1 = 0x00;   P6M0 = 0x00;                                                                   // 设置为准双向口
    23.     P7M1 = 0x00;   P7M0 = 0x00;                                                                   // 设置为准双向口
    24.                                                                                                                                   
    25.         usb_init();                                                                                                  // USB CDC 接口配置
    26.                                                                                                                                   
    27.         TM7707_StartADC();                                             
    28.                                                                                                                                   
    29.         EA = 1;                                                        
    30.         TM7707_Delay_ms(1000);                                                                                 // 延时100MS
    31.                                                                                                                                   
    32.                                                                                                                                   
    33.     while(1)                                                      
    34.     {                                                              
    35.         if(DeviceState != DEVSTATE_CONFIGURED)                                  // 等待USB完成配置
    36.             continue;                                             
    37.                                                                                                                                   
    38.         if (bUsbOutReady)                                          
    39.         {                                                         
    40.             //USB_SendData(UsbOutBuffer,OutNumber);                          // 发送数据缓冲区,长度
    41.             usb_OUT_done();                                                                            // 接收应答(固定格式)
    42.         }
    43.                 /*****************************************数据采集和发送处理************************************/               
    44.                 ADCVAL   = TM7707_ReadADC(0);
    45.                 printf("通道1电压:%.5f\r\n",ADCVAL*2.5/(16777216UL));
    46.                 TM7707_Delay_ms(100);                                                                         // 延时1000MS
    47.     }
    48. }
    复制代码





    这里增加了和cdc的串口打印结果,0.1秒打印一次,方便我们观察,


    截图202311101602573514.jpg
    数字电源设置为1V的时候,采集的数据如上



    截图202311101603386552.jpg
    数字电源设置为1.5V的时候,采集的数据如上



    截图202311101604235007.jpg
    数字电源设置为2V的时候,采集的数据如上


    当然这个还只是没有标定的结果,其实也能看出他在电压稳定的情况下的数据波动基本都在0.0005V以内,非常的稳定!

    完整代码代码还是群文件自行下载,有问题随时交流~


    回复 送花

    使用道具 举报

  • TA的每日心情
    奋斗
    昨天 08:38
  • 签到天数: 144 天

    [LV.7]常住居民III

    8

    主题

    77

    回帖

    996

    积分

    高级会员

    积分
    996
    发表于 2023-11-11 22:22:25 | 显示全部楼层
    赞一个。
    回复 送花

    使用道具 举报

  • TA的每日心情
    奋斗
    15 小时前
  • 签到天数: 154 天

    [LV.7]常住居民III

    23

    主题

    38

    回帖

    549

    积分

    高级会员

    积分
    549
    发表于 2023-11-14 08:36:33 | 显示全部楼层
    赞一个。
    回复 送花

    使用道具 举报

  • TA的每日心情
    开心
    14 小时前
  • 签到天数: 146 天

    [LV.7]常住居民III

    12

    主题

    100

    回帖

    1438

    积分

    金牌会员

    积分
    1438
    发表于 2023-11-15 14:23:35 | 显示全部楼层
    小商家的逻辑:不要问,问了就是你的错

    点评

    哈哈哈  详情 回复 发表于 2023-11-16 08:29
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    15 小时前
  • 签到天数: 90 天

    [LV.6]常住居民II

    38

    主题

    986

    回帖

    6771

    积分

    荣誉版主

    冲哥视频教程和各种开源资料QQ交流群884047237,可群

    积分
    6771
    QQ
     楼主| 发表于 2023-11-16 08:29:40 | 显示全部楼层
    gzlai 发表于 2023-11-15 14:23
    小商家的逻辑:不要问,问了就是你的错

    哈哈哈
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    奋斗
    15 小时前
  • 签到天数: 121 天

    [LV.7]常住居民III

    12

    主题

    315

    回帖

    870

    积分

    高级会员

    积分
    870
    发表于 2023-11-22 13:34:02 | 显示全部楼层
    赞一个。
    回复 送花

    使用道具 举报

  • TA的每日心情
    开心
    22 小时前
  • 签到天数: 65 天

    [LV.6]常住居民II

    5

    主题

    39

    回帖

    456

    积分

    中级会员

    积分
    456
    发表于 2023-11-29 09:27:14 | 显示全部楼层
    楼主可以测试一下TM7707真实ADC位数吗?直接测1.5V干电池电压,连续测20次,看看原始ADC数据的稳定性,手册的数据是17位。

    点评

    这么测没啥意义,17位ADC吹口气都得抖多少位了,再其次电源线,杜邦线,模块走线什么的都会影响精度的,具体的还是得看你的电路设计和防护措施、  详情 回复 发表于 2023-11-30 13:16
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    15 小时前
  • 签到天数: 90 天

    [LV.6]常住居民II

    38

    主题

    986

    回帖

    6771

    积分

    荣誉版主

    冲哥视频教程和各种开源资料QQ交流群884047237,可群

    积分
    6771
    QQ
     楼主| 发表于 2023-11-30 13:16:38 | 显示全部楼层
    hhh402 发表于 2023-11-29 09:27
    楼主可以测试一下TM7707真实ADC位数吗?直接测1.5V干电池电压,连续测20次,看看原始ADC数据的稳定性,手册 ...

    这么测没啥意义,17位ADC吹口气都得抖多少位了,再其次电源线,杜邦线,模块走线什么的都会影响精度的,具体的还是得看你的电路设计和防护措施、
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    22 小时前
  • 签到天数: 65 天

    [LV.6]常住居民II

    5

    主题

    39

    回帖

    456

    积分

    中级会员

    积分
    456
    发表于 2023-12-1 10:05:04 | 显示全部楼层
    只看稳定性,不看精度,也可以看出普通接法的稳定性,如果连17位都达不到,这个24位ADC就太差了。
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情

    14 小时前
  • 签到天数: 117 天

    [LV.6]常住居民II

    3

    主题

    204

    回帖

    558

    积分

    高级会员

    积分
    558
    发表于 2023-12-3 16:56:21 | 显示全部楼层
    哈哈,看了两遍代码才看出是咋回事来,第一眼很难定位到问题。
    回复 支持 反对 送花

    使用道具 举报

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

    本版积分规则

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

    GMT+8, 2024-4-29 23:51 , Processed in 0.092744 second(s), 70 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

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