In the past, the voltage measurements of the ADS1262 were recorded in a polling loop. A polling loop is the simplest form for performing repeated measurements. The loop reads a measurement and then waits for a certain time, e.g. 2 ms. It uses the sleep command, which does not block the CPU from processing other threads. However, this solution is still not very good for two reasons. The sleep command uses a parameter in milliseconds to wait. A parameter with milliseconds is not fine enough for our purposes and also the waiting time is not guaranteed, but mainly a minimum waiting time. Therefore, the polling loop is not exact enough and wastes a lot of precious thread time.

It would be much better, if the loop would wait until the next measurement would be finished and then continue with the execution immediately. Exactly this can be achived by using the data ready (DRDY) pin of the ADS1262 as an interrupt pin for the Raspberry Pi. Here we use BCM 25 of the Raspebeery Pi, which is GPIO pin 22, and connect it with the DRDY pin of the Protocentral breakout board by a 47 Ω resistor.

Also we need to implement some software code for programming the pin 25 as an interrupt pin, see the softweare section below.
After that, we can adjust the number of measurements per second by simply using the Set_Data_Rate procedure, which is both more elegant and more efficient than the former polling loop 🙂 .

Hardware

Circuit diagram

The DRDY pin of the Protocentral breakout board is connected with pin 22 (=25 BCM) of the Raspberry Pi GPIO header by a 47 Ω resistor.

Software

Within the file ads1262_unit.pas some code needs to be added to support the interruptprogramming.

 

Pin BCM 25 of the Raspberry Pi is used as an input interrupt pin. Since the DRDY pin turns low, when the data is ready after the measurement, pin 25 is programming to react on a falling signal, id est on interrupt mode GPIO_INTERRUPT_FALLING.

Also a function Wait_For_Interrupt (F_Timeout : Integer) is introduced, which either waits for an interrupt within a specified timeout limit or returns with FALSE if the timeout has reached.

 

Const
{ ####################################################################################### }
{ ## GPIO data ## }
{ ####################################################################################### }
GPIO_DRDY_PIN = 25;

Type
{ ####################################################################################### }
{ ## T_GPIO_Pin ## }
{ ####################################################################################### }
T_GPIO_Pin = Class(TObject)
Protected
M_GPIO_Pin : U_Int_32;
Procedure GPIO_Write_File (Const F_Filename : String; Const F_Output_Buffer_A : T_Buffer_A; F_Length : U_Int_32);
Function GPIO_Read_File (Const F_Filename : String; F_Length : U_Int_32) : String;
Procedure Set_Export (F_Export_F : Boolean);
Function Get_Direction () : T_GPIO_Direction;
Procedure Set_Direction (F_GPIO_Direction : T_GPIO_Direction);
Function Get_Value () : Boolean;
Procedure Set_Value (F_Value : Boolean);
Function Get_Interrupt_Mode () : T_GPIO_Interrupt_Mode;
Procedure Set_Interrupt_Mode (F_GPIO_Interrupt_Mode : T_GPIO_Interrupt_Mode);
Public
Constructor Create (F_GPIO_Pin : U_Int_32);
Destructor Destroy; Override;
Function Wait_For_Interrupt (F_Timeout : Integer) : Boolean;
Published
Property Direction : T_GPIO_Direction Read Get_Direction Write Set_Direction;
Property Value : Boolean Read Get_Value Write Set_Value;
Property Interrupt_Mode : T_GPIO_Interrupt_Mode Read Get_Interrupt_Mode Write Set_Interrupt_Mode;
End; { T_SPI_DEVICE }

{ ####################################################################################### }
{ ## T_ADS1262 ## }
{ ####################################################################################### }
T_ADS1262 = Class(T_SPI_Device)
Protected
M_Count : UInt64;
M_Bit_N : Integer;
M_EMA_Data : Int_32;
M_EMA_Bit_N : Double;
M_DRDY_Pin : T_GPIO_Pin;
M_Voltage_Limit : Double;
M_Sensivity : Int_32;
Public
Constructor Create ();
Destructor Destroy (); Override;
Procedure Reset_and_Init ();
Procedure Execute_Command (F_Command : U_Int_8; F_Wait_Time_msec : Int_32 = ADS1262_WAIT_TIME_MS_COMMAND);
Function Read_Register (F_Register_Adress : U_Int_8) : U_Int_8;
Procedure Write_Register (F_Register_Adress : U_Int_8; F_Data : U_Int_8);
Procedure System_Offset_Self_Calibration ();
Procedure Set_Continuous_Conversion ();
Procedure Set_Pulse_Conversion ();
Procedure Start_Conversion ();
Procedure Stop_Conversion ();
Procedure Set_POWER (F_VBIAS_Mode : U_Int_8; F_INTREF_Mode : U_Int_8);
Function Get_INPMUX () : U_Int_8;
Procedure Set_INPMUX (F_MUXP : U_Int_8; F_MUXN : U_Int_8);
Procedure Set_INPMUX_Without_Wait (F_MUXP : U_Int_8; F_MUXN : U_Int_8);
Procedure Set_PGA (F_Bypass_Mode : U_Int_8; F_Gain_Mode : U_Int_8);
Procedure Set_Data_Rate (F_Rate_Mode : U_Int_8);
Procedure Set_Filter_Mode (F_Filter_Mode : U_Int_8);
Procedure Set_Chop_Mode (F_Chop_Mode : U_Int_8);
Procedure Set_OFCAL (F_OFCAL : Int_32);
Function Read_ADC1_Data (Var F_Status : U_Int_8; Var F_CRC : U_Int_8) : Int_32;
Function Read_ADC1_Data : Int_32;
Procedure Read_All_Registers (Var F_Register_Block : T_Register_Block);
Function Calculate_Voltage (F_Data : Int_32) : Double;
Function Get_Voltage () : Double;
Function Calculate_Sensivity_Value (F_Value : Int_32; F_Sensivity : Int_32) : Int_32;
Published
Property DRDY_Pin : T_GPIO_Pin Read M_DRDY_Pin;
Property ADC1_Data : Int_32 Read Read_ADC1_Data;
Property Voltage : Double Read Get_Voltage;
Property EMA_Data : Int_32 Read M_EMA_Data;
Property EMA_Bit_N : Double Read M_EMA_Bit_N;
Property Sensivity : Int_32 Read M_Sensivity Write M_Sensivity;
End; { T_ADS1262 }


Implementation

{ ####################################################################################### }
{ ## T_GPIO_Pin ## }
{ ####################################################################################### }

{ --------------------------------------------------------------------------------------- }
Function T_GPIO_Pin.Get_Interrupt_Mode () : T_GPIO_Interrupt_Mode;
{ Get interrupt mode }
{ --------------------------------------------------------------------------------------- }
Var
Format_S : String;
S : String;

Begin { T_GPIO_Pin.Get_Interrupt_Mode }
{$IFDEF DEBUG_GPIO_OUTPUT}
WriteLn ('Begin of: T_GPIO_Pin.Get_Interrupt_Mode');
{$ENDIF}

Format_S := Format (GPIO_LINUX_GPIOPIN_DIR + 'edge', [M_GPIO_Pin]);
S := GPIO_Read_File (Format_S, 7);
Case S Of
'none' :
Begin { 'none' }
Result := [];
End; { 'none' }
'rising' :
Begin { 'rising' }
Result := [GPIO_INTERRUPT_RISING];
End; { 'rising' }
'falling' :
Begin { 'falling' }
Result := [GPIO_INTERRUPT_FALLING];
End; { 'falling' }
'both' :
Begin { 'both' }
Result := [GPIO_INTERRUPT_RISING, GPIO_INTERRUPT_FALLING];
End; { 'both' }
Else
Begin { else }
Result := [];
End; { else }
End; { Case }
End; { T_GPIO_Pin.Get_Interrupt_Mode }


{ --------------------------------------------------------------------------------------- }
Procedure T_GPIO_Pin.Set_Interrupt_Mode (F_GPIO_Interrupt_Mode : T_GPIO_Interrupt_Mode);
{ Set interrupt mode }
{ --------------------------------------------------------------------------------------- }
Var
Format_S : String;
S : String;
I : Integer;

Begin { T_GPIO_Pin.Set_Interrupt_Mode }
{$IFDEF DEBUG_GPIO_OUTPUT}
WriteLn ('Begin of: T_GPIO_Pin.Set_Interrupt_Mode');
{$ENDIF}

Format_S := Format (GPIO_LINUX_GPIOPIN_DIR + 'edge', [M_GPIO_Pin]);
I := 0;
S := 'none';
If F_GPIO_Interrupt_Mode = [] Then
Begin { then }
S := 'none';
End { then }
Else
Begin { else }
If F_GPIO_Interrupt_Mode = [GPIO_INTERRUPT_RISING] Then
Begin { then }
S := 'rising';
End { then }
Else
Begin { else }
If F_GPIO_Interrupt_Mode = [GPIO_INTERRUPT_FALLING] Then
Begin { then }
S := 'falling';
End { then }
Else
Begin { else }
If F_GPIO_Interrupt_Mode = [GPIO_INTERRUPT_RISING, GPIO_INTERRUPT_FALLING] Then
Begin { then }
S := 'both';
End; { then }
End; { else }
End; { else }
End; { else }

I := 0;
Repeat
GPIO_Write_File (Format_S, @S [1], Length (S));
Sleep (1);
Inc (I);
Until (Interrupt_Mode = F_GPIO_Interrupt_Mode) or (I > GPIO_TIMEOUT_INTERRUPTMODE_MS);

If I > GPIO_TIMEOUT_INTERRUPTMODE_MS Then
Begin { then }
ShowMessage ('Timeout in Set_Interrupt_Mode: ' + S);
Exit;
End; { then }

Sleep (GPIO_WAIT_TIME_MS_COMMAND);
End; { T_GPIO_Pin.Set_Interrupt_Mode }


{ --------------------------------------------------------------------------------------- }
Function T_GPIO_Pin.Wait_For_Interrupt (F_Timeout : Integer) : Boolean;
{ Wait for next interrupt or timeout }
{ --------------------------------------------------------------------------------------- }
Var
Format_S : String;
GPIO_File_Handle : Int_32;
File_Descriptor_Set : Array[0..0] Of PollFd;
File_Descriptor_C : Int_32;
Dummy_Content : Array[0..1] Of Char;

Begin { T_GPIO_Pin.Wait_For_Interrupt }
{$IFDEF DEBUG_GPIO_OUTPUT}
WriteLn ('Begin of: T_GPIO_Pin.Wait_For_Interrupt');
{$ENDIF}

Result := FALSE;

If Get_Interrupt_Mode () = [] Then
Begin { then }
ShowMessage ('T_GPIO_Pin.Wait_For_Interrupt: No interrupt mode set.');
Exit;
End; { then }

Format_S := Format (GPIO_LINUX_GPIOPIN_DIR + 'value', [M_GPIO_Pin]);
GPIO_File_Handle := FpOpen (Format_S, O_RDONLY or O_NONBLOCK);
If GPIO_File_Handle < 0 Then
Begin { then }
ShowMessage ('T_GPIO_Pin.Wait_For_Interrupt: Error T_GPIO_Pin.Wait_For_Interrupt:' + IntToStr (GPIO_File_Handle));
Exit;
End; { then }

FpRead (GPIO_File_Handle, Dummy_Content [0], Length (Dummy_Content));

FillByte (File_Descriptor_Set [0], SizeOf (File_Descriptor_Set), 0);
File_Descriptor_Set[0].fd := GPIO_File_Handle;
File_Descriptor_Set[0].events := POLLPRI;

File_Descriptor_C := FpPoll (@File_Descriptor_Set [0], 1, F_Timeout);
If File_Descriptor_C < 0 Then
Begin { then }
ShowMessage ('T_GPIO_Pin.Wait_For_Interrupt: Interrupt failed.');
FpClose (GPIO_File_Handle);
Exit;
End { then }
Else
Begin { else }
If File_Descriptor_C = 0 Then
Begin { then }
{ Timeout occured }
FpClose (GPIO_File_Handle);
Exit;
End; { then }
End; { else }

If (File_Descriptor_Set [0].REvents and POLLPRI) <> 0 Then
Begin { then }
{ Interrupt occured }
FpClose (GPIO_File_Handle);
Result := TRUE;
Exit;
End; { then }

FpClose (GPIO_File_Handle);
Result := TRUE;
End; { T_GPIO_Pin.Wait_For_Interrupt }


{ ####################################################################################### }
{ ## T_ADS1262 ## }
{ ####################################################################################### }

{ --------------------------------------------------------------------------------------- }
Constructor T_ADS1262.Create ();
{ Initialization of the object }
{ --------------------------------------------------------------------------------------- }
Begin { T_ADS1262.Create }
Inherited Create ();

{$IFDEF DEBUG_ADS1262_OUTPUT}
WriteLn ('Begin of : T_ADS1262.Create');
{$ENDIF}

M_DRDY_Pin := T_GPIO_Pin.Create (GPIO_DRDY_PIN);
M_DRDY_Pin.Direction := GPIO_DIRECTION_IN;
M_DRDY_Pin.Interrupt_Mode := [GPIO_INTERRUPT_FALLING];

M_Count := 0;

M_Voltage_Limit := ADS1262_MODE2_GAIN_01_LIMIT;

M_Sensivity := ADS1262_INITIAL_SENSIVITY;

Reset_and_Init ();
End; { T_ADS1262.Create }


{ --------------------------------------------------------------------------------------- }
Destructor T_ADS1262.Destroy ();
{ Free data }
{ --------------------------------------------------------------------------------------- }
Begin { T_ADS1262.Destroy }
{$IFDEF DEBUG_ADS1262_OUTPUT}
WriteLn ('Begin of : T_ADS1262.Destroy');
{$ENDIF}

M_DRDY_Pin.Free;

Execute_Command (ADS1262_COMMAND_RESET);

Inherited Destroy ();
End; { T_ADS1262.Destroy }