在做东西的过程中,因为HX711的固有问题,选了ADS1256模块,还挺贵的,硬件方面不太懂,直接用成品模块,接上管脚和GPIO就可以用了,就编程准备驱动。
从卖家那拿到的代码,还有网上下的代码,对比芯片手册看了1天和运行,硬是没有跑出来,卖家又骗人卖前说有技术支持卖了后问他们时他们说你自己看,真是气死人了。
国外的代码注释还有些时序上的说明,但很不全,有些代码也找不到了,所以也跑不出来,对照芯片手册很多理解不清,只好憋自己下力气琢磨了。
下面就是琢磨的结果,真传一句话,把从卖家给的资料案例代码进行了改造(原代码时序嵌在函数里面,外部根本没法控制,所以出问题,且没有注释上说明,没法运行),修改后,加了时序注释,非常非常清楚。
真传一句话,假传万言书。
正文:
环境:
开发板:树莓派4B 1.5GHz CPU
IDE:QT Creator 5.0
需要用到wiringPi库的delayMicroseconds函数;QMutex(看波形发现有的波形会被拉长,需要加锁)。
1. 芯片上时序要求
2. 代码 (时序在注释中了)
初始化直接用管脚 RST ,不用软方法。其余读写代码:
#include "oldADS1256.h"
#include "wiringPi.h"
#include <QMutex>
int SCLK = 26; // 串行时钟输入
int DIN = 27; // 数据转换结束标志, 低电平有效
int DOUT = 2; // 数据转换结束标志, 低电平有效
int DRDY = 3; // 数据转换结束标志, 低电平有效
int CS = 24; // 片选信号, 低电平有效
int RST = 21; // 复位信号, 低电平有效
//
void delaySomeNs(int nCnt)
{
for(int i = 0; i < nCnt; i++)
{
}
return;
}
void ADS1256_Send8Bit(unsigned char date)
{
unsigned char i;
for(i=0;i<8;i++)
{
if(date&0x80) //判断数据是否为高电平,高电平输出为1
{
DIN_H;
}
else
{
DIN_L;
}
SCLK_H; //时钟拉高,为产生下降沿做准备
delayMicroseconds(1); // t2h, t5
date<<=1; //时钟拉低,产生下降沿,数据传输完成
SCLK_L;
if(i < 7 )
{
delayMicroseconds(1); // t2l, except the last one
}
}
}
unsigned char ADS1256_Recive8Bit()
{
unsigned char i;
unsigned char date=0;
for(i=0;i<8;i++)
{
SCLK_H; //时钟拉高,为产生下降沿做准备
delayMicroseconds(1); // t2h, t8
if(digitalRead(DOUT)==HIGH) //
{
date++; //读取一个数据
}
SCLK_L;
date = date<<1;
if(i < 7)
{
delayMicroseconds(1); // t2l, except the last one
}
}
return(date);
}
void ADS1256_WriteReg(unsigned char Reg_ID,unsigned char Reg_Date)
{
ADS1256_Send8Bit(CMD_WREG|Reg_ID); //送写寄存器命令
delayMicroseconds(1); // t2l
ADS1256_Send8Bit(0X00); //只写一个寄存器
delayMicroseconds(1); // t2l
ADS1256_Send8Bit(Reg_Date); //寄存器要写的内容
}
void ADS1256_WriteCmd(unsigned char cmd) //写命令指令
{
ADS1256_Send8Bit(cmd);
}
//void ADS1256_CfgADC(unsigned char gain,unsigned char drate) //初始化设置,设置增益以及转换速率
//{
// ADS1256_WriteCmd(CMD_RESET); //写复位指令
// //while(digitalRead(DRDY));
// ADS1256_WriteReg(REG_STATUS,0XF4); //写状态,数据传输默认高位在前,启动矫正,禁止使用缓冲
// ADS1256_WriteCmd(CMD_SELFCAL); //自校准
// delayMicroseconds(20);
// {
// unsigned char buf[4];
// /* 状态寄存器定义
// Bits 7-4 ID3, ID2, ID1, ID0 Factory Programmed Identification Bits (Read Only)
// Bit 3 ORDER: Data Output Bit Order
// 0 = Most Significant Bit First (default)
// 1 = Least Significant Bit First
// Input data is always shifted in most significant byte and bit first. Output data is always shifted out most significant
// byte first. The ORDER bit only controls the bit order of the output data within the byte.
// Bit 2 ACAL : Auto-Calibration
// 0 = Auto-Calibration Disabled (default)
// 1 = Auto-Calibration Enabled
// When Auto-Calibration is enabled, self-calibration begins at the completion of the WREG command that changes
// the PGA (bits 0-2 of ADCON register), DR (bits 7-0 in the DRATE register) or BUFEN (bit 1 in the STATUS register)
// values.
// Bit 1 BUFEN: Analog Input Buffer Enable
// 0 = Buffer Disabled (default)
// 1 = Buffer Enabled
// Bit 0 DRDY : Data Ready (Read Only)
// This bit duplicates the state of the DRDY pin.
// ACAL=1使能自校准功能。当 PGA,BUFEEN, DRATE改变时会启动自校准
// */
// buf[0] = 0x04;//(0 << 3) | (1 << 2) | (0 << 1);
// /* 高四位0表示AINP接 AIN0, 低四位8表示 AINN 固定接 AINCOM */
// buf[1] = 0x01;//0x08; //通道设置选择
// /* ADCON: A/D Control Register (Address 02h)
// Bit 7 Reserved, always 0 (Read Only)
// Bits 6-5 CLK1, CLK0 : D0/CLKOUT Clock Out Rate Setting
// 00 = Clock Out OFF
// 01 = Clock Out Frequency = fCLKIN (default)
// 10 = Clock Out Frequency = fCLKIN/2
// 11 = Clock Out Frequency = fCLKIN/4
// When not using CLKOUT, it is recommended that it be turned off. These bits can only be reset using the RESET pin.
// Bits 4-2 SDCS1, SCDS0: Sensor Detect Current Sources
// 00 = Sensor Detect OFF (default)
// 01 = Sensor Detect Current = 0.5 μ A
// 10 = Sensor Detect Current = 2 μ A
// 11 = Sensor Detect Current = 10μ A
// The Sensor Detect Current Sources can be activated to verify the integrity of an external sensor supplying a signal to the
// ADS1255/6. A shorted sensor produces a very small signal while an open-circuit sensor produces a very large signal.
// Bits 2-0 PGA2, PGA1, PGA0: Programmable Gain Amplifier Setting
// 000 = 1 (default)
// 001 = 2
// 010 = 4
// 011 = 8
// 100 = 16
// 101 = 32
// 110 = 64
// 111 = 64
// */
// buf[2] = gain;//(0 << 5) | (0 << 3) | (gain << 0);
// buf[3] = drate; // DRATE_10SPS; /* 选择数据输出速率 */
// CS_L;
// delayMicroseconds(2);
// ADS1256_Send8Bit(CMD_WREG|0); //写寄存器
// ADS1256_Send8Bit(0x03); //连续写入4个数据
// ADS1256_Send8Bit(buf[0]); //设置状态寄存器
// ADS1256_Send8Bit(buf[1]); //设置输入通道参数
// ADS1256_Send8Bit(buf[2]); //设置ADCON控制寄存器,增益
// ADS1256_Send8Bit(buf[3]); //设置数据速率
// CS_H;
// delayMicroseconds(2);
// }
// delayMicroseconds(50);
//}
// 20241212, 此为读取通道(差分或单端)的函数。 在此之前,用RESET管脚发个LOW电平去RESET芯片,持续10ms,所有配置用默认的即可;
// 30ksps其实很好,可以快速的取得DRDY,响应还快,不需要调整配置,默认即可;再定时调用此函数读数即可。
// 全部函数都按时序调整正确(见delayMicroseconds函数的表达和后面的注释)并运行取数,波形很好;
// 环境:树莓派4B 1.5GHz(要用cpufrequence库来确定主频)还有wiringPi库的delayMicoseconds基本能准确延时us
long ADS1256_GetAdc(unsigned char channel)
{
long ret;
ret = 0;
while(digitalRead(DRDY));
QMutex mutex;
mutex.lock();
CS_L;
delayMicroseconds(1); // t3
ADS1256_WriteReg(REG_MUX,channel); // 写入读取的通道
delayMicroseconds(2); // t11, 4 tclkin = 0.52us, 2us ensure
ADS1256_WriteCmd(CMD_SYNC); //同步A/D转换命令
delayMicroseconds(5); // t11,24 tclkin = 3.13us, 5us ensue
ADS1256_WriteCmd(CMD_WAKEUP); //完成SYNC并退出待机模式
delayMicroseconds(2); // t10, 8 tclkin = 1.04us
CS_H;
mutex.unlock();
while(digitalRead(DRDY));
mutex.lock();
CS_L; //片选拉低
delayMicroseconds(1); // t3
ADS1256_Send8Bit(CMD_RDATA); //读取数据命令
delayMicroseconds(8); // t6, 7.68M freq, 0.13us,at least 6.5us, use 8us to ensure
//连续接收3个数据,高字节在前
ret = ((long)ADS1256_Recive8Bit() << 16);
delayMicroseconds(1); // t2l
ret +=( (long)ADS1256_Recive8Bit() << 8);
delayMicroseconds(1); // t2l
ret += ADS1256_Recive8Bit() ;
delaySomeNs(26); // t9~t10, expect to delay 0.2us
delayMicroseconds(1); // t9~t10, 8--10 tclkin, 1.04~1.3us, choose 1.2us
CS_H;
mutex.unlock();
// if(ret & 0x800000) //
// {
// ret |= 0xFFFF000000;
// }
return ret;
}
//-----------------------------------------------------------------------------
// End Of File
//-----------------------------------------------------------------------------
#ifndef __ADS1256_H_
#define __ADS1256_H_
// 定义命令,可见数据手册P34
#define CMD_WAKEUP 0x00 // Completes SYNC and Exits Standby Mode 0000 0000 (00h) 完成SYNC并退出待机状态
#define CMD_RDATA 0x01 // Read Data 0000 0001 (01h) 读取数据
#define CMD_RDATAC 0x03 // Read Data Continuously 0000 0011 (03h) 连续读取数据
#define CMD_SDATAC 0x0f // Stop Read Data Continuously 0000 1111 (0Fh) 停止连续读取数据
#define CMD_RREG 0x10 // Read from REG rrr 0001 rrrr (1xh) 从REG读取
#define CMD_WREG 0x50 // Write to REG rrr 0101 rrrr (5xh) 写到REG
#define CMD_SELFCAL 0xf0 // Offset and Gain Self-Calibration 1111 0000 (F0h) 偏移和增益自校准
#define CMD_SELFOCAL 0xf1 // Offset Self-Calibration 1111 0001 (F1h) 偏移自校准
#define CMD_SELFGCAL 0xf2 // Gain Self-Calibration 1111 0010 (F2h) 增益自校准
#define CMD_SYSOCAL 0xf3 // System Offset Calibration 1111 0011 (F3h) 系统偏移校准
#define CMD_SYSGCAL 0xf4 // System Gain Calibration 1111 0100 (F4h) 系统增益校准
#define CMD_SYNC 0xfc // Synchronize the A/D Conversion 1111 1100 (FCh) 同步A/D转换
#define CMD_STANDBY 0xfd // Begin Standby Mode 1111 1101 (FDh) 开始待机模式
#define CMD_RESET 0xfe // Reset to Power-Up Values 1111 1110 (FEh) 重置为开机值
// 定义ADS1256的内部寄存器,可见数据手册P30
#define REG_STATUS 0x00 // x1H 状态寄存器
#define REG_MUX 0x01 // 01H 输入多路复用器控制寄存器
#define REG_ADCON 0x02 // 20H A/D控制寄存器
#define REG_DRATE 0x03 // F0H A/D数据速率
#define REG_IO 0x04 // E0H GPIO控制寄存器
#define REG_OFC0 0x05 // xxH 偏移校准字节0,最低有效字节
#define REG_OFC1 0x06 // xxH 偏移校准字节1
#define REG_OFC2 0x07 // xxH 偏移校准字节2,最高有效字节
#define REG_FSC0 0x08 // xxH 满量程校准字节0,最低有效字节
#define REG_FSC1 0x09 // xxH 满量程校准字节1
#define REG_FSC2 0x0A // xxH 满量程校准字节2,最高有效字节
// 定义多路复用代号
#define MUXP_AIN0 0x00
#define MUXP_AIN1 0x10
#define MUXP_AIN2 0x20
#define MUXP_AIN3 0x30
#define MUXP_AIN4 0x40
#define MUXP_AIN5 0x50
#define MUXP_AIN6 0x60
#define MUXP_AIN7 0x70
#define MUXP_AINCOM 0x80
#define MUXN_AIN0 0x00
#define MUXN_AIN1 0x01
#define MUXN_AIN2 0x02
#define MUXN_AIN3 0x03
#define MUXN_AIN4 0x04
#define MUXN_AIN5 0x05
#define MUXN_AIN6 0x06
#define MUXN_AIN7 0x07
#define MUXN_AINCOM 0x08
// 定义增益代号
#define PGA_1 0x00
#define PGA_2 0x01
#define PGA_4 0x02
#define PGA_8 0x03
#define PGA_16 0x04
#define PGA_32 0x05
#define PGA_64 0x06
//#define ADS1256_GAIN_64 0x07
//定义转换速率代号
#define DATARATE_30K 0xF0
#define DATARATE_15K 0xE0
#define DATARATE_7_5K 0xD0
#define DATARATE_3_7_5K 0xC0
#define DATARATE_2K 0xB0
#define DATARATE_1K 0xA0
#define DATARATE_500 0x92
#define DATARATE_100 0x82
#define DATARATE_60 0x72
#define DATARATE_50 0x63
#define DATARATE_30 0x53
#define DATARATE_25 0x43
#define DATARATE_15 0x33
#define DATARATE_10 0x23
#define DATARATE_5 0x13
#define DATARATE_2_5 0x02
//-----------------------------------------------------------------
// I/O口定义
//-----------------------------------------------------------------
extern int SCLK; // 串行时钟输入
extern int DIN; // 数据转换结束标志, 低电平有效
extern int DOUT; // 数据转换结束标志, 低电平有效
extern int DRDY; // 数据转换结束标志, 低电平有效
extern int CS; // 片选信号, 低电平有效
extern int RST; // 复位信号, 低电平有效
//sbit SCLK=P1^0; // 串行时钟输入
//sbit DIN=P1^1; // 数据转换结束标志, 低电平有效
//sbit DOUT=P1^2; // 数据转换结束标志, 低电平有效
//sbit DRDY=P1^3; // 数据转换结束标志, 低电平有效
//sbit CS=P1^4; // 片选信号, 低电平有效
//sbit RST=P1^5; // 复位信号, 低电平有效
#define RST_L (digitalWrite(RST,LOW))
#define RST_H (digitalWrite(RST,HIGH))
#define CS_L (digitalWrite(CS,LOW))
#define CS_H (digitalWrite(CS,HIGH))
#define DRDY_L (digitalWrite(DRDY,LOW))
#define DRDY_H (digitalWrite(DRDY,HIGH))
#define DOUT_L (digitalWrite(DOUT,LOW))
#define DOUT_H (digitalWrite(DOUT,HIGH))
#define DIN_L (digitalWrite(DIN,LOW))
#define DIN_H (digitalWrite(DIN,HIGH))
#define SCLK_L (digitalWrite(SCLK,LOW))
#define SCLK_H (digitalWrite(SCLK,HIGH))
//-----------------------------------------------------------------
// 外部函数声明
//-----------------------------------------------------------------
extern void ADS1256_Send8Bit(unsigned char date);
extern unsigned char ADS1256_Recive8Bit(void);
extern void ADS1256_WriteReg(unsigned char Reg_ID,unsigned char Reg_Date);
extern void ADS1256_WriteCmd(unsigned char cmd);
// extern signed long ADS1256_ReadData(unsigned char channel);
// extern void ADS1256_Init(void);
//extern void ADS1256_CfgADC(unsigned char gain,unsigned char drate);
extern long ADS1256_GetAdc(unsigned char channel);
extern void delayNs(int nCnt);
// extern unsigned char ADS1256_ReadChipID(void);
// extern void ADS1256_SetChannal(unsigned char ch);
// extern void ADS1256_SetDiffChannal(unsigned char ch);
// extern int ADS1256_ReadData(void);
// extern int ADS1256_ReadAdc(unsigned char ch);
// extern unsigned char ADS1256_ReadReg(unsigned char Reg_ID);
// extern void ADS1256_WaitDRDY(void);
// extern void ADS1256_StartScan(unsigned char ucScanMode);
// extern void ADS1256_StartScan_1(void);
// extern void ADS1256_StopScan(void);
// extern int ADS1256_GetAdc(unsigned char ch);
// extern void ADS1256_ISR(void);
// extern void ADS1256_CfgADC(unsigned char gain, unsigned char drate);
// extern void ADS1256_ResetHard(void);
// signed long ADS1256ReadData(unsigned char channel);
// void ADS1256_Init(void); //初始化ADS1256
// void Init_ADS1256_GPIO(void); //
#endif
要点:
1. 时序不由函数内部控制,函数内部如sendbit只保证这段时序,整体时序由外部调用的来按时序要求进行控制;将SCLK的时序控制了,上升沿到最后一个下降沿截止满足要求,后续相邻的时延由外部调用函数按时序控制;
2. t9和t10,有点矛盾,8--10个tclkin,不是很好控制,用波形凑吧。
3. tclkin是ADS1256上用的1/晶振频率,7.68M,所以tclkin=0.13us。 tdata是采样速率,这个时间比较长,一般达不到。
实测数据能很轻松拿到,注意时序就没啥问题,其余的内容参照寄存器、命令很简单了。