电子DIY小家 发表于 2023-11-10 16:07:15

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

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

      

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

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



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

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






当然这个原理图比较简单,可以自己对照手册去看下每个引脚的含义,这里就不多介绍了,只有特别注意的就是这个基准电压源是2.5V的,也就是这个ADC的测量范围就是0-2.5V。
   模块            单片机
TM7707_CS   <>    P4^6;      
TM7707_RESET<>    P4^5;      
TM7707_DIN    <>    P4^0;      
TM7707_SCK    <>    P4^3;      
TM7707_DOUT   <>    P4^1;      
TM7707_DRDY    <>    P4^7;      接线方式如上所示~用的STC32G单片机内部的硬件SPI,自己移植到自己项目的时候一定要注意引脚,如果移植到了没有硬件SPI的引脚的话要把代码改为软件模拟SPI的方式驱动!!

硬件解决了,那么下面来看软件,这个模块非常的简单,只有很少的几个寄存器,这里直接一个个看下来了:
1.通信寄存器:(所有寄存器的选择都是通过这个寄存器为枢纽来选择的~放在早些年这就算是个哨兵!)


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


基于这个通道选择位,我们可以直接在代码里增加如下的宏定义来给我选择,这样可以保证寄存器出错的概率降到最低:
#define REG_COMM      0X00                                                                                 // 通信寄存器
#define REG_SETUP   0X10                                                                                 // 设置寄存器
#define REG_CLOCK   0X20                                                                                 // 时钟寄存器
#define REG_DATA      0X30                                                                                 // 数据寄存器
#define REG_ZERO_CH10X60                                                                                 // CH1 偏移寄存器
#define REG_FULL_CH10X70                                                                                 // CH1 满量程寄存器
#define REG_ZERO_CH20X61                                                                                 // CH2 偏移寄存器
#define REG_FULL_CH20X71                                                                                 // CH1 满量程寄存器 相应的读写位和通道选择位也可以用宏定义来选择:
#define WRITE         0X00                                                                              // 写操作
#define READ          0X08                                                                              // 读操作

#define CH_0          0                                                                                       // 通道0
#define CH_1          1                                                                                       // 通道1

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


这里先来看模式:

这里的四种模式就可以用宏定义写成这样的格式:#define MD_NORMAL          (0 << 6)                                                                        // 正常模式
#define      MD_CAL_SELF          (1 << 6)                                                                        // 自校准模式
#define      MD_CAL_ZERO          (2 << 6)                                                                        // 校准0刻度模式
#define      MD_CAL_FULL          (3 << 6)                                                                        // 校准满刻度模式


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



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



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




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



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


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

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




经过上面的梳理,其实初始化就变的特别简单了//========================================================================
// 函数名称: TM7707_InitADC
// 函数功能: 配置SPI接口,用于连接 TM7707
// 入口参数: 无
// 函数返回: 无
// 其他说明:
//========================================================================
void TM7707_InitADC(void)
{
      SPI_Init1();
      
      TM7707_CS = 0;
      TM7707_Delay_ms(10);                                                                                                         // 延时10MS
      TM7707_RST();                                                                                                                         // 硬件复位
                                                                                                
                                                                                                               
                                                                                                
      TM7707_Delay_ms(5);                                                                                                             // 延时10MS 在接口序列丢失的情况下,如果在DIN 高电平的写操作持续了足够长的时间(至少 32个串行时钟周期),TM7707 将会回到默认状态
      TM7707_SyncSPI();                                                                                                               // 同步TM7707 SPI时序
      TM7707_Delay_ms(5);                                                                                                             // 延时10MS

      TM7707_WriteByte(REG_CLOCK | WRITE | CH_0);                                                                // 先写通信寄存器,下一步是写时钟寄存器
      TM7707_WriteByte(CLKDIS_0 | CLK_4_9152M | FS_50HZ);                                                // 4.9152MHZ晶振 刷新速率50Hz

      
      TM7707_CalibSelf(0);                                                                                                         // 内部自校准 CH0每次上电进行一次自校准
      TM7707_Delay_ms(10);                                                                                                         // 延时10MS
      TM7707_CalibSelf(1);                                                                                                         // 内部自校准 CH0
      TM7707_Delay_ms(10);                                                                                                         // 延时10MS
}



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

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



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

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

上面的都是初始化的配置,然后是正常的adc采集://========================================================================
// 函数名称: TM7707_ReadADC
// 函数功能: 读取ADC指定通道的数据
// 入口参数: 无
// 函数返回: 返回的24位adc数据
// 其他说明:
//========================================================================
u32 TM7707_ReadADC(u8 ch)
{
      u32 ADC_V;
      u8 i,a,b,c;


      for(i = 0;i < 2;i++)                                                                                                      // 为了避免通道切换造成读数失效,读2次 */
      {
                TM7707_WaitDRDY();                                                                                                      // 等待DRDY口线为0

                if(ch == 0)
                {
                        TM7707_WriteByte(0x38);      
                }
                else if(ch == 1)
                {
                        TM7707_WriteByte(0x39);
                }

                a = TM7707_Read1Byte();
                b = TM7707_Read1Byte();
                c = TM7707_Read1Byte();

                ADC_V = a;
                ADC_V = ADC_V<<8|b;
                ADC_V = ADC_V<<8|c;
      }
      
      return ADC_V;
}

代码如上,其实就是读取那个寄存器的三个字节的数据,回到开头的问题,其实读取的三个字节分别是



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

最后看到主函数://========================================================================
// 函数名称: main
// 函数功能: 主函数
// 入口参数: 无
// 函数返回: 无
// 其他说明:
//========================================================================
void main(void)
{
      u32 ADCVAL;
      
    WTST = 0;                                                                                                          // 设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1;                                                                                                         // 扩展寄存器(XFR)访问使能
    CKCON = 0;                                                                                                         // 提高访问XRAM速度
                                                                                                                                 
    P0M1 = 0x00;   P0M0 = 0x00;                                                                   // 设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;                                                                   // 设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;                                                                   // 设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;                                                                   // 设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;                                                                   // 设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;                                                                   // 设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;                                                                   // 设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;                                                                   // 设置为准双向口
                                                                                                                                 
      usb_init();                                                                                                // USB CDC 接口配置
                                                                                                                                 
      TM7707_StartADC();                                             
                                                                                                                                 
      EA = 1;                                                      
      TM7707_Delay_ms(1000);                                                                                 // 延时100MS
                                                                                                                                 
                                                                                                                                 
    while(1)                                                      
    {                                                            
      if(DeviceState != DEVSTATE_CONFIGURED)                                  // 等待USB完成配置
            continue;                                             
                                                                                                                                 
      if (bUsbOutReady)                                          
      {                                                         
            //USB_SendData(UsbOutBuffer,OutNumber);                        // 发送数据缓冲区,长度
            usb_OUT_done();                                                                            // 接收应答(固定格式)
      }
                /*****************************************数据采集和发送处理************************************/               
                ADCVAL   = TM7707_ReadADC(0);
                printf("通道1电压:%.5f\r\n",ADCVAL*2.5/(16777216UL));
                TM7707_Delay_ms(100);                                                                         // 延时1000MS
    }
}




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


数字电源设置为1V的时候,采集的数据如上



数字电源设置为1.5V的时候,采集的数据如上




数字电源设置为2V的时候,采集的数据如上


当然这个还只是没有标定的结果,其实也能看出他在电压稳定的情况下的数据波动基本都在0.0005V以内,非常的稳定!
完整代码代码还是群文件自行下载,有问题随时交流~

NTC 发表于 2023-11-11 22:22:25

赞一个。

xujibicool 发表于 2023-11-14 08:36:33

赞一个。

gzlai 发表于 2023-11-15 14:23:35

小商家的逻辑:不要问,问了就是你的错

电子DIY小家 发表于 2023-11-16 08:29:40

gzlai 发表于 2023-11-15 14:23
小商家的逻辑:不要问,问了就是你的错

哈哈哈{:lol:}

jwg 发表于 2023-11-22 13:34:02

赞一个。

hhh402 发表于 2023-11-29 09:27:14

楼主可以测试一下TM7707真实ADC位数吗?直接测1.5V干电池电压,连续测20次,看看原始ADC数据的稳定性,手册的数据是17位。

电子DIY小家 发表于 2023-11-30 13:16:38

hhh402 发表于 2023-11-29 09:27
楼主可以测试一下TM7707真实ADC位数吗?直接测1.5V干电池电压,连续测20次,看看原始ADC数据的稳定性,手册 ...

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

hhh402 发表于 2023-12-1 10:05:04

只看稳定性,不看精度,也可以看出普通接法的稳定性,如果连17位都达不到,这个24位ADC就太差了。

Snapdragon 发表于 2023-12-3 16:56:21

哈哈,看了两遍代码才看出是咋回事来,第一眼很难定位到问题。
页: [1] 2
查看完整版本: 32G12K128 驱动 TM7707的24位ΣADC开源教程