找回密码
 立即注册
查看: 3804|回复: 17

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

[复制链接]
  • 打卡等级:常住居民III
  • 打卡总天数:135
  • 最近打卡:2024-12-25 10:15:05

47

主题

1350

回帖

8058

积分

荣誉版主

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

积分
8058
QQ
发表于 2023-11-10 16:07: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以内,非常的稳定!

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


回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:464
  • 最近打卡:2025-04-29 08:25:45

10

主题

130

回帖

564

积分

高级会员

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

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:469
  • 最近打卡:2025-04-10 16:59:05

45

主题

69

回帖

1223

积分

金牌会员

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

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:449
  • 最近打卡:2025-05-01 08:38:15

12

主题

110

回帖

1072

积分

金牌会员

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

点评

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

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:135
  • 最近打卡:2024-12-25 10:15:05

47

主题

1350

回帖

8058

积分

荣誉版主

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

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

哈哈哈
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:403
  • 最近打卡:2025-04-29 08:28:43

12

主题

319

回帖

2017

积分

金牌会员

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

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:415
  • 最近打卡:2025-05-01 09:38:35

10

主题

70

回帖

1307

积分

金牌会员

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

点评

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

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:135
  • 最近打卡:2024-12-25 10:15:05

47

主题

1350

回帖

8058

积分

荣誉版主

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

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

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

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:415
  • 最近打卡:2025-05-01 09:38:35

10

主题

70

回帖

1307

积分

金牌会员

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

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:338
  • 最近打卡:2025-05-01 00:00:50

4

主题

242

回帖

1287

积分

金牌会员

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

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2025-5-1 20:00 , Processed in 0.193614 second(s), 125 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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