Skip to content

Instantly share code, notes, and snippets.

@tomas789
Last active January 29, 2025 16:44
Show Gist options
  • Save tomas789/184d4a5a21a223f3076872683c9e594f to your computer and use it in GitHub Desktop.
Save tomas789/184d4a5a21a223f3076872683c9e594f to your computer and use it in GitHub Desktop.
ADS1263 on Raspberry PI 5 (C++)

Code is intended for the Waveshare High-Precision AD HAT

It is adopted from the manufacturer's repo

I have added passive waiting for the ready signal. The original code implements active waiting which means that one core is always at 100% load. With the pasive waiting I ran read 7200 SPS at around 24% load of a single core.

Test sampling rates

SPI at 2 MHz. According to the datasheet, it can do up to 8 MHz.

SPS settings C++ - active C++ - pasive Python
38400 SPS 9174 - 12820 SPS 8771 - 10101 SPS 10375.80 - 11876.16 SPS
14400 SPS 7194 - 9615 SPS 5263 - 7246 SPS 8680 - 11815 SPS
7200 SPS 6060 - 7246 SPS 6493 - 7246 SPS error
4800 SPS 4672 - 4807 SPS 4098 - 4807 SPS error

Compile

clang++ -std=c++11 ads1263.cpp -llgpio -O3 && ./a.out
#include <iostream>
#include <chrono>
#include <thread>
#include <lgpio.h>
#include <chrono>
#include <iomanip>
#include <functional>
#include <mutex>
#include <condition_variable>
const int RST_PIN = 18;
const int CS_PIN = 22;
const int DRDY_PIN = 17;
#define DEBUG_MODE 0
#define LOG(msg) \
if (DEBUG_MODE) \
{ \
msg; \
}
#define CHECK_ERROR(error) \
if ((error) < 0) \
{ \
const char *error_str = lguErrorText(error); \
throw std::runtime_error("lguErrorText: " + std::string(error_str)); \
}
void delay_ms(int ms)
{
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
// void ADS1263_reset(int chip)
// {
// int error;
// CHECK_ERROR(lgGpioWrite(chip, RST_PIN, 1));
// delay_ms(200);
// CHECK_ERROR(lgGpioWrite(chip, RST_PIN, 0));
// delay_ms(200);
// CHECK_ERROR(lgGpioWrite(chip, RST_PIN, 1));
// delay_ms(200);
// }
// void digital_write(int chip, int pin, int value)
// {
// CHECK_ERROR(lgGpioWrite(chip, pin, value));
// }
// int digital_read(int chip, int pin)
// {
// int value = lgGpioRead(chip, pin);
// CHECK_ERROR(value);
// return value;
// }
// void ADS1263_ReadData(int chip, spi, int reg)
// {
// int error;
// digital_write(chip, CS_PIN, 0);
// lgSpiWrite(spiHandle)
// }
void afunc(int e, lgGpioAlert_p evt, void *data);
class Config
{
public:
Config()
{
int spiDevice = 0;
int spiChannel = 0;
int spiBaud = 2000000;
spiHandle = lgSpiOpen(spiDevice, spiChannel, spiBaud, 1);
CHECK_ERROR(spiHandle);
chip = lgGpiochipOpen(4);
CHECK_ERROR(chip);
lgChipInfo_s chipInfo;
CHECK_ERROR(lgGpioGetChipInfo(chip, &chipInfo));
LOG(std::cout << "Chip: " << chipInfo.name << std::endl);
LOG(std::cout << " - lines: " << chipInfo.lines << std::endl);
LOG(std::cout << " - label: " << chipInfo.label << std::endl);
LOG(std::cout << "Config initialized" << std::endl);
}
static Config& getInstance()
{
static Config config;
return config;
}
void digital_write(int pin, int value)
{
LOG(std::cout << " * digital_write(" << pin << ", " << value << ") at: " << chip << std::endl);
LOG(std::cout << " - chip: " << chip << std::endl);
lgLineInfo_t lineInfo;
CHECK_ERROR(lgGpioGetLineInfo(chip, pin, &lineInfo));
LOG(std::cout << " - lineInfo: " << lineInfo.name << std::endl);
CHECK_ERROR(lgGpioWrite(chip, pin, value));
LOG(std::cout << " - DONE" << std::endl);
}
int digital_read(int pin)
{
// std::cout << " * digital_read" << std::endl;
int value = lgGpioRead(chip, pin);
CHECK_ERROR(value);
return value;
}
void delay_ms(int ms)
{
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
void spi_writebytes(std::initializer_list<uint8_t> data)
{
LOG(std::cout << " * spi_writebytes" << std::endl);
char *txBuf = (char *)malloc(data.size());
std::copy(data.begin(), data.end(), txBuf);
CHECK_ERROR(lgSpiWrite(spiHandle, txBuf, data.size()));
free((void *)txBuf);
}
char *spi_readbytes(int n_bytes)
{
LOG(std::cout << " * spi_readbytes" << std::endl);
char *rxBuf = (char *)malloc(n_bytes);
int n_bytes_read = lgSpiRead(spiHandle, rxBuf, n_bytes);
CHECK_ERROR(n_bytes_read);
if (n_bytes_read != n_bytes)
{
printf("Failed to read %d bytes\n", n_bytes);
std::exit(1);
}
return rxBuf;
}
void spi_readbytes_n(uint8_t *rxBuf, unsigned int n)
{
int n_bytes_read = lgSpiRead(spiHandle, (char *)rxBuf, n);
CHECK_ERROR(n_bytes_read);
if (n_bytes_read != n)
{
printf("Failed to read %d bytes\n", n);
std::exit(1);
}
}
void receive_alert(int e, lgGpioAlert_p evt, void *data)
{
int i;
int userdata = *(int *)data;
for (i = 0; i < e; i++)
{
LOG(printf("u=%d t=%" PRIu64 " c=%d g=%d l=%d f=%d (%d of %d)\n",
userdata, evt[i].report.timestamp, evt[i].report.chip,
evt[i].report.gpio, evt[i].report.level,
evt[i].report.flags, i + 1, e));
// if (evt[i].report.chip != chip)
// continue;
if (evt[i].report.gpio != DRDY_PIN)
continue;
if (evt[i].report.flags != 0)
continue;
{
std::lock_guard<std::mutex> lock(m);
is_ready = evt[i].report.level == 0;
cv.notify_all();
}
}
}
void module_init()
{
int lFlags = 0;
CHECK_ERROR(lgGpioClaimOutput(chip, lFlags, RST_PIN, 0));
CHECK_ERROR(lgGpioClaimOutput(chip, lFlags, CS_PIN, 0));
CHECK_ERROR(lgGpioClaimInput(chip, LG_SET_PULL_UP, DRDY_PIN));
is_ready = digital_read(DRDY_PIN) == 0;
static int userdata = 123;
// Create a static function wrapper to handle the bind
CHECK_ERROR(lgGpioSetAlertsFunc(chip, DRDY_PIN, afunc, this));
CHECK_ERROR(lgGpioClaimAlert(chip, 0, LG_SET_PULL_UP | LG_BOTH_EDGES, DRDY_PIN, -1));
}
void wait_for_ready()
{
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [this]() { return is_ready; });
}
void describe_pin(int pin)
{
lgLineInfo_t lineInfo;
CHECK_ERROR(lgGpioGetLineInfo(chip, pin, &lineInfo));
LOG(std::cout << " * describe_pin(" << pin << ") at: " << chip << " " << lineInfo.name << std::endl);
}
// ~Config()
// {
// if (chip != std::numeric_limits<int>::max())
// {
// lgGpiochipClose(chip);
// }
// if (spiHandle != std::numeric_limits<int>::max())
// {
// lgSpiClose(spiHandle);
// }
// }
private:
int spiHandle = std::numeric_limits<int>::max();
int chip = std::numeric_limits<int>::max();
std::mutex m;
std::condition_variable cv;
bool is_ready = false;
};
void afunc(int e, lgGpioAlert_p evt, void *data) {
Config& config = Config::getInstance();
config.receive_alert(e, evt, data);
}
void print_sbc_name()
{
char *sbcName = (char *)malloc(100);
int error = lguSbcName(sbcName, 100);
CHECK_ERROR(error);
printf("SBC name: %s\n", sbcName);
free(sbcName);
}
uint8_t ADS1263_CMD_RESET = 0x06;
uint8_t ADS1263_CMD_START1 = 0x08;
uint8_t ADS1263_CMD_STOP1 = 0x0A;
uint8_t ADS1263_CMD_START2 = 0x0C;
uint8_t ADS1263_CMD_STOP2 = 0x0E;
uint8_t ADS1263_CMD_RDATA1 = 0x12;
uint8_t ADS1263_CMD_RDATA2 = 0x14;
uint8_t ADS1263_CMD_SYOCAL1 = 0x16;
uint8_t ADS1263_CMD_SYGCAL1 = 0x17;
uint8_t ADS1263_CMD_SFOCAL1 = 0x19;
uint8_t ADS1263_CMD_SYOCAL2 = 0x1B;
uint8_t ADS1263_CMD_SYGCAL2 = 0x1C;
uint8_t ADS1263_CMD_SFOCAL2 = 0x1E;
uint8_t ADS1263_CMD_RREG = 0x20;
uint8_t ADS1263_CMD_RREG2 = 0x00;
uint8_t ADS1263_CMD_WREG = 0x40;
uint8_t ADS1263_CMD_WREG2 = 0x00;
uint8_t ADS1263_REG_ID = 0x00;
uint8_t ADS1263_REG_POWER = 0x01;
uint8_t ADS1263_REG_INTERFACE = 0x02;
uint8_t ADS1263_REG_MODE0 = 0x03;
uint8_t ADS1263_REG_MODE1 = 0x04;
uint8_t ADS1263_REG_MODE2 = 0x05;
uint8_t ADS1263_REG_INPMUX = 0x06;
uint8_t ADS1263_REG_OFCAL0 = 0x07;
uint8_t ADS1263_REG_OFCAL1 = 0x08;
uint8_t ADS1263_REG_OFCAL2 = 0x09;
uint8_t ADS1263_REG_FSCAL0 = 0x0A;
uint8_t ADS1263_REG_FSCAL1 = 0x0B;
uint8_t ADS1263_REG_FSCAL2 = 0x0C;
uint8_t ADS1263_REG_IDACMUX = 0x0D;
uint8_t ADS1263_REG_IDACMAG = 0x0E;
uint8_t ADS1263_REG_REFMUX = 0x0F;
uint8_t ADS1263_REG_TDACP = 0x10;
uint8_t ADS1263_REG_TDACN = 0x11;
uint8_t ADS1263_REG_GPIOCON = 0x12;
/* ... */
uint8_t ADS1263_DRATE_38400SPS = 0xF;
uint8_t ADS1263_DRATE_19200SPS = 0xE;
uint8_t ADS1263_DRATE_14400SPS = 0xD;
uint8_t ADS1263_DRATE_7200SPS = 0xC;
uint8_t ADS1263_DRATE_4800SPS = 0xB;
uint8_t ADS1263_DRATE_2400SPS = 0xA;
uint8_t ADS1263_DRATE_1200SPS = 0x9;
uint8_t ADS1263_DRATE_400SPS = 0x8;
uint8_t ADS1263_DRATE_100SPS = 0x7;
uint8_t ADS1263_DRATE_60SPS = 0x6;
uint8_t ADS1263_DRATE_50SPS = 0x5;
uint8_t ADS1263_DRATE_20SPS = 0x4;
uint8_t ADS1263_DRATE_16d6SPS = 0x3;
uint8_t ADS1263_DRATE_10SPS = 0x2;
uint8_t ADS1263_DRATE_5SPS = 0x1;
uint8_t ADS1263_DRATE_2d5SPS = 0x0;
uint8_t ADS1263_GAIN_1 = 0; // GAIN 1
uint8_t ADS1263_GAIN_2 = 1; // GAIN 2
uint8_t ADS1263_GAIN_4 = 2; // GAIN 4
uint8_t ADS1263_GAIN_8 = 3; // GAIN 8
uint8_t ADS1263_GAIN_16 = 4; // GAIN 16
uint8_t ADS1263_GAIN_32 = 5; // GAIN 32
uint8_t ADS1263_GAIN_64 = 6; // GAIN 64
uint8_t ADS1263_DELAY_0s = 0;
uint8_t ADS1263_DELAY_8d7us = 1;
uint8_t ADS1263_DELAY_17us = 2;
uint8_t ADS1263_DELAY_35us = 3;
uint8_t ADS1263_DELAY_169us = 4;
uint8_t ADS1263_DELAY_139us = 5;
uint8_t ADS1263_DELAY_278us = 6;
uint8_t ADS1263_DELAY_555us = 7;
uint8_t ADS1263_DELAY_1d1ms = 8;
uint8_t ADS1263_DELAY_2d2ms = 9;
uint8_t ADS1263_DELAY_4d4ms = 10;
uint8_t ADS1263_DELAY_8d8ms = 11;
class ADS1263
{
public:
ADS1263(bool use_active_wait = false)
{
LOG(std::cout << "ADS1263 initialized" << std::endl);
ScanMode = 1;
use_active_wait = use_active_wait;
}
void ADS1263_reset()
{
LOG(std::cout << "ADS1263_reset" << std::endl);
Config& config = Config::getInstance();
config.digital_write(RST_PIN, 1);
delay_ms(200);
config.digital_write(RST_PIN, 0);
delay_ms(200);
config.digital_write(RST_PIN, 1);
delay_ms(200);
}
char *ADS1263_ReadData(uint8_t reg)
{
LOG(std::cout << "ADS1263_ReadData" << std::endl);
Config& config = Config::getInstance();
LOG(std::cout << " * digital_write(CS_PIN, 0)" << std::endl);
config.digital_write(CS_PIN, 0);
LOG(std::cout << " * spi_writebytes" << std::endl);
config.spi_writebytes({static_cast<uint8_t>(ADS1263_CMD_RREG | reg), 0x00});
LOG(std::cout << " * spi_readbytes" << std::endl);
char *data = config.spi_readbytes(1);
LOG(std::cout << " * digital_write(CS_PIN, 1)" << std::endl);
config.digital_write(CS_PIN, 1);
return data;
}
void ADS1263_WriteCmd(uint8_t cmd)
{
LOG(std::cout << "ADS1263_WriteCmd" << std::endl);
Config& config = Config::getInstance();
config.digital_write(CS_PIN, 0);
config.spi_writebytes({cmd});
config.digital_write(CS_PIN, 1);
}
void ADS1263_WriteReg(uint8_t reg, uint8_t data)
{
LOG(std::cout << "ADS1263_WriteReg" << std::endl);
Config& config = Config::getInstance();
config.digital_write(CS_PIN, 0);
config.spi_writebytes({static_cast<uint8_t>(ADS1263_CMD_WREG | reg), 0x00, data});
config.digital_write(CS_PIN, 1);
}
void ADS1263_ConfigADC(uint8_t gain, uint8_t drate)
{
LOG(std::cout << "ADS1263_ConfigADC" << std::endl);
uint8_t mode2 = 0x80; // 0x80:PGA bypassed, 0x00:PGA enabled
mode2 |= (gain << 4) | drate;
ADS1263_WriteReg(ADS1263_REG_MODE2, mode2);
auto mode2_read = ADS1263_ReadData(ADS1263_REG_MODE2);
if (mode2_read[0] == mode2)
{
LOG(std::cout << "REG_MODE2 success" << std::endl);
}
else
{
LOG(std::cout << "REG_MODE2 failed" << std::endl);
}
uint8_t refmux = 0x00; // 0x00:+-2.5V as REF, 0x24:VDD,VSS as REF
ADS1263_WriteReg(ADS1263_REG_REFMUX, refmux);
auto refmux_read = ADS1263_ReadData(ADS1263_REG_REFMUX);
if (refmux_read[0] == refmux)
{
LOG(std::cout << "REG_REFMUX success" << std::endl);
}
else
{
LOG(std::cout << "REG_REFMUX failed" << std::endl);
}
uint8_t mode0 = ADS1263_DELAY_35us;
ADS1263_WriteReg(ADS1263_REG_MODE0, mode0);
auto mode0_read = ADS1263_ReadData(ADS1263_REG_MODE0);
if (mode0_read[0] == mode0)
{
LOG(std::cout << "REG_MODE0 success" << std::endl);
}
else
{
LOG(std::cout << "REG_MODE0 failed" << std::endl);
}
uint8_t mode1 = 0x84; // Digital Filter; 0x84:FIR, 0x64:Sinc4, 0x44:Sinc3, 0x24:Sinc2, 0x04:Sinc1
ADS1263_WriteReg(ADS1263_REG_MODE1, mode1);
auto mode1_read = ADS1263_ReadData(ADS1263_REG_MODE1);
if (mode1_read[0] == mode1)
{
LOG(std::cout << "REG_MODE1 success" << std::endl);
}
else
{
LOG(std::cout << "REG_MODE1 failed" << std::endl);
}
}
void ADS1263_SetDiffChannal(uint8_t channal)
{
uint8_t inpmux = 0x00;
switch (channal)
{
case 0:
inpmux = (0 << 4) | 1; // DiffChannal AIN0-AIN1
break;
case 1:
inpmux = (2 << 4) | 3; // DiffChannal AIN2-AIN3
break;
case 2:
inpmux = (4 << 4) | 5; // DiffChannal AIN4-AIN5
break;
case 3:
inpmux = (6 << 4) | 7; // DiffChannal AIN6-AIN7
break;
case 4:
inpmux = (8 << 4) | 9; // DiffChannal AIN8-AIN9
break;
}
ADS1263_WriteReg(ADS1263_REG_INPMUX, inpmux);
auto inpmux_read = ADS1263_ReadData(ADS1263_REG_INPMUX);
if (inpmux_read[0] == inpmux)
{
LOG(std::cout << "REG_INPMUX success" << std::endl);
}
else
{
LOG(std::cout << "REG_INPMUX failed" << std::endl);
}
}
void ADS1263_SetChannel(uint8_t channel)
{
if (channel > 10)
{
return;
}
uint8_t inpmux = (channel << 4) | 0x0a;
ADS1263_WriteReg(ADS1263_REG_INPMUX, inpmux);
auto inpmux_read = ADS1263_ReadData(ADS1263_REG_INPMUX);
if (inpmux_read[0] == inpmux)
{
LOG(std::cout << "REG_INPMUX success" << std::endl);
}
else
{
LOG(std::cout << "REG_INPMUX failed" << std::endl);
}
}
int ADS1263_ReadChipID()
{
LOG(std::cout << "ADS1263_ReadChipID" << std::endl);
char *id = ADS1263_ReadData(ADS1263_REG_ID);
int chip_id = id[0] >> 5;
free(id);
return chip_id;
}
void ADS1263_SetMode(uint8_t mode)
{
ScanMode = mode;
}
void ADS1263_SetUseActiveWait(bool use_active_wait)
{
this->use_active_wait = use_active_wait;
}
void ADS1263_WaitDRDY()
{
if (use_active_wait)
{
Config& config = Config::getInstance();
for (uint64_t i = 0;; i++)
{
if (config.digital_read(DRDY_PIN) == 0)
{
break;
}
if (i == 400000)
{
LOG(std::cout << "Time out ... " << std::endl);
break;
}
}
}
else
{
Config& config = Config::getInstance();
config.wait_for_ready();
}
}
uint32_t ADS1263_CheckSum(uint64_t val, uint64_t byt)
{
uint8_t sum = 0;
uint8_t mask = 0xff;
while (val)
{
sum += val & mask;
val = val >> 8;
}
sum += 0x9b;
return (sum & 0xff) ^ byt;
}
uint32_t ADS1263_Read_ADC_Data(bool *is_error = nullptr)
{
Config& config = Config::getInstance();
config.digital_write(CS_PIN, 0);
while (true)
{
config.spi_writebytes({ADS1263_CMD_RDATA1});
if ((config.spi_readbytes(1)[0] & 0x40) != 0)
{
break;
}
}
uint8_t buf[5];
// auto *buf = config.spi_readbytes(5);
config.spi_readbytes_n(buf, 5);
config.digital_write(CS_PIN, 1);
uint32_t read = (buf[0] << 24) & 0xff000000;
read |= (buf[1] << 16) & 0xff0000;
read |= (buf[2] << 8) & 0xff00;
read |= (buf[3]) & 0xff;
uint8_t CRC = buf[4];
if (ADS1263_CheckSum(read, CRC) != 0)
{
LOG(std::cout << "ADC1 data read error!" << std::endl);
if (is_error != nullptr)
{
*is_error = true;
}
return read;
}
if (is_error != nullptr)
{
*is_error = false;
}
return read;
}
uint32_t ADS1263_GetChannalValue(uint8_t channel, bool *is_error = nullptr)
{
if (ScanMode == 0)
{ // # 0 Single-ended input 10 channel Differential input 5 channel
if (channel > 10)
{
LOG(std::cout << "The number of channels must be less than 10" << std::endl);
return 0;
}
ADS1263_SetChannel(channel);
ADS1263_WaitDRDY();
return ADS1263_Read_ADC_Data(is_error);
}
else
{
if (channel > 4)
{
LOG(std::cout << "The number of channels must be less than 5" << std::endl);
return 0;
}
ADS1263_SetDiffChannal(channel);
ADS1263_WaitDRDY();
return ADS1263_Read_ADC_Data(is_error);
}
}
void ADS1263_init_ADC1(uint8_t rate)
{
LOG(std::cout << "ADS1263_init_ADC1" << std::endl);
ADS1263_reset();
int id = ADS1263_ReadChipID();
LOG(std::cout << "Chip ID: " << id << std::endl);
if (id == 0x01)
{
LOG(std::cout << "ID Read success" << std::endl);
}
else
{
LOG(std::cout << "ID Read failed" << std::endl);
}
Config& config = Config::getInstance();
ADS1263_WriteCmd(ADS1263_CMD_STOP1);
ADS1263_ConfigADC(ADS1263_GAIN_1, rate);
ADS1263_WriteCmd(ADS1263_CMD_START1);
}
private:
int ScanMode;
bool use_active_wait;
};
int main()
{
print_sbc_name();
Config& config = Config::getInstance();
config.module_init();
config.describe_pin(RST_PIN);
config.describe_pin(CS_PIN);
config.describe_pin(DRDY_PIN);
ADS1263 ads1263;
ads1263.ADS1263_SetUseActiveWait(false);
ads1263.ADS1263_init_ADC1(ADS1263_DRATE_14400SPS);
ads1263.ADS1263_SetDiffChannal(0);
auto start = std::chrono::high_resolution_clock::now();
ads1263.ADS1263_WaitDRDY();
uint64_t ok_counter = 0;
uint64_t error_counter = 0;
for (uint64_t i = 0;; ++i)
{
bool is_error = false;
ads1263.ADS1263_WaitDRDY();
auto value = ads1263.ADS1263_Read_ADC_Data(&is_error);
// auto value = ads1263.ADS1263_GetChannalValue(0, &is_error);
if (is_error)
{
// std::cout << "Error" << std::endl;
// continue;
error_counter++;
}
else
{
ok_counter++;
}
// std::cout << "Value " << i << ": " << value << std::endl;
double ref = 2.5;
if (value >> 23 == 1)
{
double v = (ref * 2.0 - double(value) * ref / 0x80000000);
// std::cout << "Value: " << v << std::endl;
}
else
{
double v = double(value) * ref / 0x7fffffff;
// std::cout << "Value: " << v;
// if (is_error) {
// std::cout << " (Error)";
// }
// std::cout << std::endl;
}
if (i % 1000 == 0)
{
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
double sps = 1000.0 / (duration.count() / 1000.0);
std::cout << "Time: " << std::fixed << std::setfill(' ') << std::setw(7) << std::setprecision(2) << double(duration.count()) / 1000.0 << " seconds." << std::endl;
std::cout << "SPS: " << std::fixed << std::setfill(' ') << std::setw(7) << std::setprecision(0) << sps << std::endl;
std::cout << "OK: " << std::fixed << std::setfill(' ') << std::setw(7) << std::setprecision(0) << ok_counter << std::endl;
std::cout << "Error: " << std::fixed << std::setfill(' ') << std::setw(7) << std::setprecision(0) << error_counter << std::endl;
ok_counter = 0;
error_counter = 0;
start = end;
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment