开题之前先给大家看一段花絮,背景是我在一家店买了个TM7707的模块,再调驱动的时候ADC数值一直和我的理论值有极大地偏差,最后一点点定位到了错误的地方,发现就是店家的示例代码的问题,然后我去找店家据理力争:(图1是店家自信的回复,图2是店家提供的示例代码,图3是24位adc的三个8位数据按照店家代码的合并过程)
当然,最后店家也意识到了问题,已经及时把示例代码改正了!(说实话我还是比较喜欢这里这个老板这里桀骜不驯,迷之自信的态度的哈哈~ )
有人可能说为什么单片机内部明明有有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_CH1 0X60 // CH1 偏移寄存器
- #define REG_FULL_CH1 0X70 // CH1 满量程寄存器
- #define REG_ZERO_CH2 0X61 // CH2 偏移寄存器
- #define REG_FULL_CH2 0X71 // 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以内,非常的稳定!
完整代码代码还是群文件自行下载,有问题随时交流~
|