/*
Belegung des GPIO im Gesamtprojekt (Temperatur-)Logger
G___F___E___D__C____B____A__________A______B___C____D___F___G___
3P3 3P3 3,3V | | 5V 5P
SDA SDA GPIO2 | | 5V 5P 5P 5P
SCL TA SCL GPIO3 | | GND DGND GND GND
RCK GPIO4 | | GPIO14 TX EOP
GND GND GND | | GPIO15 RX
O1 DRDY GPIO17 | | GPIO18 RST O2
O3 PDWN GPIO27 | | GND DGND GND
O4 CS0 GPIO22 | | GPIO23 CS1 O5
3P3 3,3V | | GPIO24 O6
DIN DIN MOSI GPIO10 | | GND DGND GND
DOUT MISO GPIO9 | | GPIO25 O7
SCK SCLK SCK GPIO11 | | GPIO8 CE0
GND DGND GND | | GPIO7 CE1
idD GPIO0 | | GPIO1 idC
GPIO5 | | GND DGND GND
A7 H7-4 GPIO6 | | GPIO12 A6
A5 H7-3 GPIO13 | | GND DGND GND
A4 H7-2 GPIO19 | | GPIO16 A3
A2 H7-1 GPIO26 | | GPIO20 A1
GND DGND GND | | GPIP21 A0
G = DAC8 für Secon XPL (hier nur Digitalausgänge mit ULN2003), nur „messpi1“
F = Analogmultiplexer-Vorsatzplatine
E = Echtzeituhr mit DS3231
D = Shutdown-Taster (TA) und End-Of-Powerdown (EOP)
C = A/D+D/A-Karte mit ADS1256 von HighResolution
B = GPIO-Zusatzfunktionen
A = GPIO-Ports
*/
#include <bcm2835.h>
#include <stdio.h>
#include <signal.h>
enum gpio{
DRDY = 17, // Eingang, Low = A/D-Konversion fertig
RST = 18, // Ausgang, Low = Reset
CS0 = 22, // Ausgang, Low = A/D-Konverter adressieren
CS1 = 23, // Ausgang, Low = D/A-Konverter adressieren
PDWN = 27, // Ausgang: Low = Stromsparmodus
};
inline void CS(uint8_t lh) {bcm2835_gpio_write(CS0,lh);}
inline bool RDY() {return !bcm2835_gpio_lev(DRDY);}
//inline void RESET(uint8_t lh) {bcm2835_gpio_write(RST,lh);}
struct ADS1256_t{
/*Register definition: Table 23. Register Map --- ADS1256 datasheet Page 30*/
enum REG{ /*Register address, followed by reset the default values */
STATUS, // x1H
MUX, // 01H
ADCON, // 20H
DRATE, // F0H
IO, // E0H
OFC0, // undefined
OFC1,
OFC2,
FSC0,
FSC1,
FSC2,
};
/* Command definition: TTable 24. Command Definitions --- ADS1256 datasheet Page 34 */
enum CMD{
WAKEUP = 0x00, // Completes SYNC and Exits Standby Mode
RDATA = 0x01, // Read Data
RDATAC = 0x03, // Read Data Continuously
SDATAC = 0x0F, // Stop Read Data Continuously
RREG = 0x10, // Read from REG r (1rh)
WREG = 0x50, // Write to REG r (5rh)
SELFCAL = 0xF0, // Offset and Gain Self-Calibration
SELFOCAL= 0xF1, // Offset Self-Calibration
SELFGCAL= 0xF2, // Gain Self-Calibration
SYSOCAL = 0xF3, // System Offset Calibration
SYSGCAL = 0xF4, // System Gain Calibration
SYNC = 0xFC, // Synchronize the A/D Conversion
STANDBY = 0xFD, // Begin Standby Mode
RESET = 0xFE, // Reset to Power-Up Values
};
const int DRAE_COUNT = 15;
enum GAIN_e{
GAIN_1,
GAIN_2,
GAIN_4,
GAIN_8,
GAIN_16,
GAIN_32,
GAIN_64,
}Gain; /* GAIN */
enum RATE_e{
S30000,
S15000,
S7500,
S3750,
S2000,
S1000,
S500,
S100,
S60,
S50,
S30,
S25,
S15,
S10,
S5,
S2d5,
RATE_MAX
}Rate; /* DATA output speed*/
static const float rates[16];
int32_t AdcNow[8]; /* ADC conversion value */
uint8_t Channel; /* The current channel*/
uint8_t ScanMode; /*Scanning mode, 0 Single-ended input 8 channel, 1 Differential input 4 channel*/
void StartScan(uint8_t _ucScanMode);
void CfgADC(GAIN_e _gain, RATE_e _rate);
static void DelayDATA();
static void WaitDRDY();
static void WriteReg(uint8_t _RegID, uint8_t _RegValue);
static uint8_t ReadReg(uint8_t _RegID);
static void WriteCmd(uint8_t _cmd);
static uint8_t ReadChipID();
static void SetChannel(uint8_t _ch);
static void SetDiffChannel(uint8_t _ch);
static int32_t ReadData();
int32_t GetAdc(uint8_t _ch) const;
void ISR();
bool Scan();
}ADS1256;
const float ADS1256_t::rates[16]={
30000,15000,7500,3750,2000,1000,500,100,60,50,30,25,15,10,5,2.5};
void bsp_DelayUS(uint64_t micros) {
bcm2835_delayMicroseconds (micros);
}
// Configuration of the STM32 GPIO and SPI interface, The connection ADS1256
void bsp_InitADS1256(void) {
#ifdef SOFT_SPI
CS(1);
SCK_0();
DI_0();
#endif
//CfgADC(GAIN_1,S1000); /* 配置ADC参数: 增益1:1, 数据输出速率 1KHz */
}
// Configuration DRDY PIN for external interrupt is triggered
// _ucDiffMode : 0 Single-ended input 8 channel, 1 Differential input 4 channe
void ADS1256_t::StartScan(uint8_t _ucScanMode) {
ScanMode = _ucScanMode;
/* 开始扫描前, 清零结果缓冲区 */
Channel = 0;
for (uint8_t i = 0; i < 8; i++) AdcNow[i] = 0;
}
// SPI bus to send 8 bit data
static void spiSendByte(uint8_t _data) {
bsp_DelayUS(2);
bcm2835_spi_transfer(_data);
}
//delay time: wait for automatic calibration
void ADS1256_t::WaitDRDY() {
for (uint32_t i = 0; i < 400000; i++) {
if (RDY()) return;
}
printf("ADS1256_t::WaitDRDY() Time Out ...\n");
}
// The configuration parameters of ADC, gain and data rate
// _gain: select gain from 1 to 64
// _drate: select one of the possible data rates
void ADS1256_t::CfgADC(GAIN_e _gain, RATE_e _rate) {
Gain = _gain;
Rate = _rate;
WaitDRDY();
/*Status register define
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 enable calibration
*/
//WriteReg(REG::STATUS, (0 << 3) | (1 << 2) | (1 << 1));
/* 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-3 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
*/
//WriteReg(REG::ADCON, (0 << 5) | (0 << 2) | (GAIN_1 << 1)); /*choose 1: gain 1 ;input 5V/
CS(0); /* SPI片选 = 0 */
spiSendByte(CMD::WREG | 0); /* Write command register, send the register address */
spiSendByte(0x03); /* Register number 4,Initialize the number -1*/
spiSendByte(1<<2); /* Set the status register */
spiSendByte(0x08); /* Set the input channel parameters */
spiSendByte(_gain); /* Set the ADCON control register,gain */
static const uint8_t tabRate[] = {
0xF0,0xE0,0xD0,0xC0,0xB0,0xA1,0x92,0x82,0x72,0x63,0x53,0x43,0x33,0x20,0x13,0x03};
spiSendByte(tabRate[_rate]); /* Set the output rate */
CS(1); /* SPI cs = 1 */
bsp_DelayUS(50);
}
void ADS1256_t::DelayDATA() {
/* Delay from last SCLK edge for DIN to first SCLK rising edge for DOUT: RDATA, RDATAC,RREG Commands
min 50 CLK = 50 * 0.13uS = 6.5uS
*/
bsp_DelayUS(10); /* The minimum time delay 6.5us */
}
// SPI bus receive function
static uint8_t spiRecvByte() {
return bcm2835_spi_transfer(0xff);
}
// Write the corresponding register
// _RegID: register ID
// _RegValue: register Value
void ADS1256_t::WriteReg(uint8_t _RegID, uint8_t _RegValue) {
CS(0); /* SPI cs = 0 */
spiSendByte(CMD::WREG|_RegID); /*Write command register */
spiSendByte(0x00); /*Write the register number */
spiSendByte(_RegValue); /*send register value */
CS(1); /* SPI cs = 1 */
}
// Read the corresponding register
// _RegID: register ID
// return: read register value
uint8_t ADS1256_t::ReadReg(uint8_t _RegID) {
CS(0); /* SPI cs = 0 */
spiSendByte(CMD::RREG|_RegID); /* Write command register */
spiSendByte(0x00); /* Write the register number */
DelayDATA(); /*delay time */
uint8_t read = spiRecvByte(); /* Read the register values */
CS(1); /* SPI cs = 1 */
return read;
}
// Sending a single byte order
void ADS1256_t::WriteCmd(uint8_t _cmd) {
CS(0); /* SPI cs = 0 */
spiSendByte(_cmd);
CS(1); /* SPI cs = 1 */
}
// Read the chip ID
// return: four high status register bits
uint8_t ADS1256_t::ReadChipID() {
WaitDRDY();
return ReadReg(REG::STATUS)>>4;
}
// Select Configuration channel number
void ADS1256_t::SetChannel(uint8_t _ch) {
/*
Bits 7-4 PSEL3, PSEL2, PSEL1, PSEL0: Positive Input Channel (AINP) Select
0000 = AIN0 (default)
0001 = AIN1
0010 = AIN2
0011 = AIN3
0100 = AIN4
0101 = AIN5
0110 = AIN6
0111 = AIN7
1xxx = AINCOM
NOTE: When using an ADS1255 make sure to only select the available inputs.
Bits 3-0 NSEL3, NSEL2, NSEL1, NSEL0: Negative Input Channel (AINN)Select
0000 = AIN0
0001 = AIN1 (default)
0010 = AIN2
0011 = AIN3
0100 = AIN4
0101 = AIN5
0110 = AIN6
0111 = AIN7
1xxx = AINCOM
*/
WriteReg(REG::MUX, _ch<<4 | 0x08); /* Bit3 = 1, AINN connection AINCOM */
}
// konfiguriert differenzielle Kanäle AIN0-AIN1, AIN2-AIN3, AIN4-AIN5 oder AIN6-AIN7
// (nur diese!)
void ADS1256_t::SetDiffChannel(uint8_t _ch) {
WriteReg(REG::MUX,_ch<<5|_ch<<1|1);
}
// function: read ADC value
int32_t ADS1256_t::ReadData() {
CS(0);
spiSendByte(CMD::RDATA); /* read ADC command */
DelayDATA();
/*Read the sample results 24bit*/
int32_t read = spiRecvByte();
read<<=8;
read|= spiRecvByte();
read<<=8;
read|= spiRecvByte();
CS(1);
read<<=8; // Extend to signed number
read>>=8;
return read;
}
// read cached ADC value
// parameter: channel number 0--7
int32_t ADS1256_t::GetAdc(uint8_t _ch) const{
if (_ch > 7) return 0;
return AdcNow[_ch];
}
// Collection procedure
void ADS1256_t::ISR() {
switch (ScanMode) {
case 0: { // Single-ended 8 channel
SetChannel(Channel);
bsp_DelayUS(5);
WriteCmd(CMD::SYNC);
bsp_DelayUS(5);
WriteCmd(CMD::WAKEUP);
bsp_DelayUS(25);
AdcNow[Channel-1&7] = ReadData();
Channel=Channel+1&7;
}break;
case 1: { /*DiffChannel*/
SetDiffChannel(Channel);
bsp_DelayUS(5);
WriteCmd(CMD::SYNC);
bsp_DelayUS(5);
WriteCmd(CMD::WAKEUP);
bsp_DelayUS(25);
AdcNow[Channel-1&3] = ReadData();
Channel=Channel+1&3;
}break;
}
}
bool ADS1256_t::Scan() {
if (RDY()) {
ISR();
return true;
}
return false;
}
bool abort;
void sig_handler(int) {
abort=true;
}
int main() {
const uint8_t ch_num=8;
if (!bcm2835_init()) return 1;
bcm2835_spi_begin();
// bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST);
bcm2835_spi_setDataMode(BCM2835_SPI_MODE1);
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_256);
bcm2835_gpio_fsel(CS0, BCM2835_GPIO_FSEL_OUTP);
CS(1);
bcm2835_gpio_fsel(DRDY, BCM2835_GPIO_FSEL_INPT);
bcm2835_gpio_set_pud(DRDY, BCM2835_GPIO_PUD_UP);
bcm2835_gpio_fsel(RST, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_write(RST,1);
bcm2835_gpio_fsel(CS1, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_write(CS1,1);
bcm2835_gpio_fsel(PDWN, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_write(PDWN,1);
//ADS1256.WriteReg(REG::MUX,0x01);
//ADS1256.WriteReg(REG::ADCON,0x20);
// ADS1256.CfgADC(ADS1256.GAIN_1,ADS1256.S15);
uint8_t id = ADS1256.ReadChipID();
printf("ASD1256 Chip ID = 0x%d (\33[1;%s\33[0m)\n",
id, id==3?"32mOK":"31mError");
signal(SIGINT,sig_handler);
ADS1256.CfgADC(ADS1256.GAIN_1, ADS1256.S50);
ADS1256.StartScan(0);
printf("\n\n\n\n\n\n\n"); // 7x
while (!abort) {
while (!ADS1256.Scan());
uint8_t i=ADS1256.Channel-2&7;
int32_t adc = ADS1256.GetAdc(i);
float volt = adc * 5.988E-7F; // 100/167 µV
printf("%d = %06X, %8ld (%.6f V)\n", i, adc&0xFFFFFF, adc, volt);
if (i==7)
printf("\33[%dA", ch_num); // Kursor hoch
bsp_DelayUS(19000);
}
bcm2835_spi_end();
bcm2835_close();
for (uint8_t i=ADS1256.Channel-1&7;i<8;i++) putchar('\n');
return 0;
}
Detected encoding: UTF-8 | 0
|