This post was last edited by Xiaomi Zha on 2022-10-16 16:09
1. Introduction of the Work
The main control part of this system is based on ST's STM32F750, and the ECG signal acquisition uses ADI's ADAS1000 series chips. The two together constitute this system. The MCU will collect the data, perform calculations, and transmit the data to the host computer via USB, and the host computer will display the data. Note that the chips used here are not self-made, but official development boards, and their names are STM32F7508-DK (official provision of this competition) and ADAS1000SDZ (engineer face recognition).
2. System Block Diagram
The system function block diagram is as above. Since two development boards are used, the communication interface SPI between the boards is directly connected with DuPont wires. For the communication between STM32 and PC, since STM32F7508-DK comes with two Micro AB connectors, the design uses HS high-speed communication interface CN12 and PC connection. Since the collection of ECG electrocardiogram signals is used in medical treatment, it cannot be used in practice. The electrical design in medical treatment must comply with relevant national requirements. For details, please refer to GB 9706.1-2020
3. Functional description of each part
The above picture shows the development board of ST's STM32F7508-DK. The system is powered by JP2. In the experiment, I provided Vin=5V, and the board has an LDO, so the core voltage of the system is 3.3V, which can be confirmed in the system schematic diagram. CN4-CN7 mainly provides some external interfaces. The official description is as follows:
I mainly used SPI2 to communicate with ADAS1000.
The figure above shows the development board of ADAS1000. The official description of each interface is as follows:
In fact, I only care about the part that I want to connect to the ST board, which is explained as follows:
- Power supply:
From the description of J5, we can see that different power supplies are needed in the system. It is troublesome to use external power supply. It is better to directly choose J7 or J9 to supply 5V power directly. As mentioned above, ST's development board Vin=5V, which is convenient for experiments.
- SPI interface:
Here you can see that there are two groups of SPI interfaces. Since two ADAS1000s are used on the development board to implement ten leads, and this experiment only implements five leads (RA RL LA LL V1) to observe ECG signals, combined with the description of each module in the above figure, we chose ADAS1000 MASTER, and combined with the schematic diagram, we can choose the SPI0 related interface.
- Connection with the human body
As can be seen in the figure above, the ECG ELECTRODE Connector interface is a connection line interface, using a DB15 interface, and the interface definition is as follows:
As mentioned above, five leads are actually used, so it is necessary to fly out Pin9-RA Pin10-LA Pin11-LL Pin12-V1 Pin14-RLD, connect the electrodes and stick them on the human body.
4. Source Code
4.1 Communication Protocol
4.1.1 Protocol Format
Header (1 byte)
|
Device address (1 byte)
|
Command (1 byte)
|
Data length (2 bytes)
|
Data (nbyte)
|
CRC(2byte)
|
0x55
|
The device address starts at 1, and 0xFF represents the broadcast address.
|
|
High byte first.
|
|
Calculate CRC16 from the header
|
4.1.2 Start ADAS1000 measurement command: 0x06
Send from host computer
|
Start flag (1 byte)
|
|
1: Enable 0: Disable
|
Lower machine reply
|
Reply (1 byte)
- There are two phases of response to a launch, the accept state and the execute state.
- For closure, just reply with acceptance status.
|
|
1: Accept success 0: Accept failure
3: Execution successful 2: Execution failed
(Expandable failure reason)
|
4.1.3 ECG Data 0x07
Actively uploaded from the lower computer to the upper computer 600/500 sps
Channel flag (2 bytes)
|
Self-incrementing serial number
(4 bytes)
|
Number of data groups (1 byte)
|
Data Group 1 Channel 0
(4byte)
|
Data set 1 channel n
(4byte)
|
Data Group 2 Channel 0
(4byte)
|
Data set 2 channel n
(4byte)
|
…
|
Bit0-10:
0: disable
1: enable
Bit14-15:
0:600sps
1:500sps
|
Starting from 0
|
|
Floating point values
|
Floating point values
|
Floating point values
|
Floating point values
|
|
4.1.4 Sending filtered data 0x04
Self-incrementing serial number (4 bytes)
|
Bandpass filter data 1
|
Bandpass filter data 2
|
Differential filter data 1
|
Differential filter data 2
|
Dynamic minimum peak value
|
Dynamic noise value
|
Starting from 0
|
Floating point values
|
Floating point values
|
Floating point values
|
Floating point values
|
Floating point values
|
Floating point values
|
4.1.5 Enable filter data upload 0x05
Send from host computer
|
Enable flag (1 byte)
|
|
1: Enable 0: Disable
|
Lower machine reply
|
Reply (1 byte)
|
|
1: Accept success 0: Accept failure
|
4.1.6 ADAS1000 status upload, 0x0C
The lower computer actively uploads (set to 0 for normal, set to 1 for abnormal). Once an abnormality occurs, the lower computer will upload once every 4 seconds; it will stop sending when it returns to normal.
STATUS (1 byte)
[0:5]
|
ADC_STATUS (1 byte)
[0:2] (out of range)
|
DCLO_HI_STATUS (1byte)[5:0]
Leads off
|
DCLO_LO_STATUS (1byte) [5:0]
Lead short circuit or grounding
|
Bit0: Data overflow
Bit1: Device internal error
Bit2: Lead off (not used)
Bit3: DC lead off
Bit4: ADC out of range
Bit5: Data CRC error
|
Bit0: LA
Bit1: LL
Bit2: RA
|
Bit0: RLD.
Bit1: LA.
Bit2: LL.
Bit3: RA.
|
Bit0: RLD.
Bit1: LA.
Bit2: LL.
Bit3: RA.
|
4.1.7 The host computer obtains CHIP status, 0x0D
Send from host computer
|
Without content
|
|
|
Lower machine reply
|
ADAS1000 operation (1 byte)
|
STATUS (1 byte)
[0:5]
|
ADC_STATUS (1 byte)
[0:2]
|
DCLO_HI_STATUS (1byte)[5:0]
Leads off
|
DCLO_LO_STATUS (1byte) [5:0]
Lead short circuit or grounding
|
AD5940 operation (1 byte)
|
STATUS (4 bytes)
|
|
0: Idle
1: At work
|
Bit0: Data overflow
Bit1: Device internal error
Bit2: Lead off (not used)
Bit3: DC lead off
Bit4: ADC out of range
Bit5: Data CRC error
|
Bit0: LA
Bit1: LL
Bit2: RA
|
Bit0: RLD.
Bit1: LA.
Bit2: LL.
Bit3: RA.
|
Bit0: RLD.
Bit1: LA.
Bit2: LL.
Bit3: RA.
|
0: Idle
1: At work
|
Bit4, ADC Minimum Value
Bit5, ADC Maximum Value
Bit6, ADC Delta Ready
Bit17, Sequencer Timeout Command Error.
Bit23, Data FIFO Full Interrupt.
Bit29, Outlier Int
|
ADAS Drivers:
#include "ADAS1000.h"
/*****************************************************************************/
/************************ Variables Definitions ******************************/
/*****************************************************************************/
static unsigned long frameSize = 0; //ADAS1000 frame size in bytes
static unsigned long frameRate = 0; //ADAS1000 frame rate
static unsigned long inactiveWordsNo = 0; //number of inactive words in a frame
bool ADAS1000_StartMea()
{
unsigned int reg = 0;
//store the selected frame rate
frameRate = ADAS1000_2KHZ_FRAME_RATE;
// Reset the ADAS1000.
//ADAS1000_SoftwareReset();
ADAS1000_HardwareReset();
//Set the frame rate
if (!ADAS1000_SetFrameRate(frameRate, 2)) //2kHz, skip=1x
{
return false;
}
//Reduce the frame size to the minimum
inactiveWordsNo = ADAS1000_FRMCTL_PACEDIS | ADAS1000_FRMCTL_RESPMDIS |
ADAS1000_FRMCTL_RESPPHDIS |
ADAS1000_FRMCTL_GPIODIS;
ADAS1000_SetInactiveFrameWords(inactiveWordsNo);
//Configure VCM = WCT = (LA+LL+RA)/3. RLD is enabled onto RL, Shield amplifier enabled.
reg = ADAS1000_CMREFCTL_SHLDEN | ADAS1000_CMREFCTL_RLD_EN | ADAS1000_CMREFCTL_DRVCM |
ADAS1000_CMREFCTL_LACM | ADAS1000_CMREFCTL_LLCM | ADAS1000_CMREFCTL_RACM;
ADAS1000_SetRegisterValue(ADAS1000_CMREFCTL, reg);
//DC Leads off enabled, leads off current 0x05 50nA
reg = ADAS1000_LOFFCTL_LOFFEN | (0x07<<2); //0x07 70na
ADAS1000_SetRegisterValue(ADAS1000_LOFFCTL, reg);
//Powers up device, enables all ECG channels in gain GAIN1:2.1 1.4, 00 = GAIN 0 = x1.4, 01 = GAIN 1 = x2.1, 10 = GAIN 2 = x2.8, 11 = GAIN 3 = x4.2 */
//low noise mode, Master device using XTAL input source
reg = ADAS1000_ECGCTL_PWREN | ADAS1000_ECGCTL_CNVEN | ADAS1000_ECGCTL_HP | ADAS1000_ECGCTL_MASTER |
ADAS1000_ECGCTL_VREFBUF | (1<<8) | ADAS1000_ECGCTL_LAEN |
ADAS1000_ECGCTL_LLEN | ADAS1000_ECGCTL_RAEN; // | ADAS1000_ECGCTL_V1EN | ADAS1000_ECGCTL_V2EN;
ADAS1000_SetRegisterValue(ADAS1000_ECGCTL, reg);
return true;
}
bool ADAS1000_StopMea()
{
// Reset the ADAS1000.
ADAS1000_HardwareReset();
return true;
}
/***************************************************************************//**
* @brief Reads the value of the selected register
*
* @param regAddress - The address of the register to read.
* @param regVal - Pointer to a variable where to store the read data.
*
* @return None.
*******************************************************************************/
void ADAS1000_GetRegisterValue(unsigned char regAddress,
unsigned long* regVal)
{
unsigned char readCmd[4] = {0, 0, 0, 0};
unsigned char readData[4] = {0, 0, 0, 0};
// Select the register (For register reads, data is shifted out
// during the next word).
readCmd[0] = regAddress; // Register address.
ADAS1000_CsClr();
ADAS1000_Delay1us(10);
ADAS1000_Write(readCmd, 4);
// Read the data from the device.
ADAS1000_Read(readData, 4);
ADAS1000_CsSet();
*regVal = ((unsigned long)readData[1] << 16) +
((unsigned long)readData[2] << 8) +
((unsigned long)readData[3] << 0);
}
/***************************************************************************//**
* @brief Writes a value to the selected register
*
* @param regAddress - The address of the register to write to.
* @param regValue - The value to write to the register.
*
* @return None.
*******************************************************************************/
void ADAS1000_SetRegisterValue(unsigned char regAddress,
unsigned long regVal)
{
unsigned char writeCmd[4] = {0, 0, 0, 0};
writeCmd[0] = 0x80 + regAddress; // Write bit and register address.
writeCmd[1] = (unsigned char)((regVal & 0xFF0000) >> 16);
writeCmd[2] = (unsigned char)((regVal & 0x00FF00) >> 8);
writeCmd[3] = (unsigned char)((regVal & 0x0000FF) >> 0);
ADAS1000_CsClr();
ADAS1000_Delay1us(10);
ADAS1000_Write(writeCmd, 4);
ADAS1000_CsSet();
}
/***************************************************************************//**
* @brief Resets the ADAS1000 part.
*
* @return None.
*******************************************************************************/
void ADAS1000_SoftwareReset(void)
{
// Clear all registers to their reset value.
ADAS1000_SetRegisterValue(ADAS1000_ECGCTL, ADAS1000_ECGCTL_SWRST);
// The software reset requires a NOP command to complete the reset.
ADAS1000_SetRegisterValue(ADAS1000_NOP, 0);
ADAS1000_Delay1us(3000);
}
void ADAS1000_HardwareReset(void)
{
ADAS1000_RstClr();
ADAS1000_Delay1us(500);
ADAS1000_RstSet(); //下降沿开始复位, drdy goes high,return low when the reset complete
ADAS1000_Delay1us(3000); //最大1.5ms
}
/***************************************************************************//**
* @brief Selects which words are not included in a data frame.
*
* @param channelsMask - Specifies the words to be excluded from the data
* frame using a bitwise or of the corresponding bits
* from the Frame Control Register.
*
* @return None.
*******************************************************************************/
void ADAS1000_SetInactiveFrameWords(unsigned long wordsMask)
{
unsigned long frmCtrlRegVal = 0;
unsigned char i = 0;
// Read the current value of the Frame Control Register
ADAS1000_GetRegisterValue(ADAS1000_FRMCTL, &frmCtrlRegVal);
//set the inactive channles
frmCtrlRegVal &= ~ADAS1000_FRMCTL_WORD_MASK;
frmCtrlRegVal |= wordsMask;
// Write the new value to the Frame Coontrol register.
ADAS1000_SetRegisterValue(ADAS1000_FRMCTL, frmCtrlRegVal);
//compute the number of inactive words
inactiveWordsNo = 0;
for(i = 0; i < 32; i++)
{
if(wordsMask & 0x00000001ul)
{
inactiveWordsNo++;
}
wordsMask >>= 1;
}
//compute the new frame size
switch(frameRate)
{
case ADAS1000_16KHZ_FRAME_RATE:
frameSize = (ADAS1000_16KHZ_WORD_SIZE / 8) *
(ADAS1000_16KHZ_FRAME_SIZE - inactiveWordsNo);
break;
case ADAS1000_128KHZ_FRAME_RATE:
frameSize = (ADAS1000_128KHZ_WORD_SIZE / 8) *
(ADAS1000_128KHZ_FRAME_SIZE - inactiveWordsNo);
break;
case ADAS1000_31_25HZ_FRAME_RATE:
frameSize = ((ADAS1000_31_25HZ_WORD_SIZE / 8) *
(ADAS1000_31_25HZ_FRAME_SIZE - inactiveWordsNo)) / 100;
break;
default: // ADAS1000_2KHZ__FRAME_RATE
frameSize = (ADAS1000_2KHZ_WORD_SIZE / 8) *
(ADAS1000_2KHZ_FRAME_SIZE - inactiveWordsNo);
break;
}
}
bool ADAS1000TestFirstComm()
{
unsigned long frmCtrlRegVal = 0;
ADAS1000_GetRegisterValue(ADAS1000_FRMCTL, &frmCtrlRegVal);
if (frmCtrlRegVal != 0x079000)
{
return false;
}
return true;
}
/***************************************************************************//**
* @brief Sets the frame rate.
*
* @param rate - ADAS1000 frame rate.
*
* @return None.
*******************************************************************************/
bool ADAS1000_SetFrameRate(unsigned long rate, unsigned char skip)
{
unsigned long frmCtrlRegVal = 0;
// Store the selected frame rate
frameRate = rate;
// Read the current value of the Frame Control Register
ADAS1000_GetRegisterValue(ADAS1000_FRMCTL, &frmCtrlRegVal);
if (frmCtrlRegVal != 0x079000)
{
return false;
}
frmCtrlRegVal &= ~ADAS1000_FRMCTL_FRMRATE_MASK;
// Compute the new frame size and update the Frame Control Register value
switch(frameRate)
{
case ADAS1000_16KHZ_FRAME_RATE:
frameSize = (ADAS1000_16KHZ_WORD_SIZE / 8) *
(ADAS1000_16KHZ_FRAME_SIZE - inactiveWordsNo);
frmCtrlRegVal |= ADAS1000_FRMCTL_FRMRATE_16KHZ;
break;
case ADAS1000_128KHZ_FRAME_RATE:
frameSize = (ADAS1000_128KHZ_WORD_SIZE / 8) *
(ADAS1000_128KHZ_FRAME_SIZE - inactiveWordsNo);
frmCtrlRegVal |= ADAS1000_FRMCTL_FRMRATE_128KHZ;
break;
case ADAS1000_31_25HZ_FRAME_RATE:
frameSize = ((ADAS1000_31_25HZ_WORD_SIZE / 8) *
(ADAS1000_31_25HZ_FRAME_SIZE - inactiveWordsNo)) / 100;
frmCtrlRegVal |= ADAS1000_FRMCTL_FRMRATE_31_25HZ;
break;
default: // ADAS1000_2KHZ__FRAME_RATE
frameSize = (ADAS1000_2KHZ_WORD_SIZE / 8) *
(ADAS1000_2KHZ_FRAME_SIZE - inactiveWordsNo);
frmCtrlRegVal |= ADAS1000_FRMCTL_FRMRATE_2KHZ;
break;
}
frmCtrlRegVal |= (skip<<2);
// Write the new Frame control Register value
ADAS1000_SetRegisterValue(ADAS1000_FRMCTL, frmCtrlRegVal);
//frmCtrlRegVal = 0;
//ADAS1000_GetRegisterValue(ADAS1000_FRMCTL, &frmCtrlRegVal);
//ADAS1000_Delay1us(1);
return true;
}
bool ADAS1000_ReadHeader()
{
unsigned char readCmd[4] = {0, 0, 0, 0};
// Select the register (For register reads, data is shifted out
// during the next word).
readCmd[0] = ADAS1000_FRAMES; // Register address.
ADAS1000_CsClr();
ADAS1000_Delay1us(1);
ADAS1000_Write(readCmd, 4);
ADAS1000_CsSet();
return true;
}
bool ADAS1000_ReadDataOne(unsigned char* pDataBuffer, int len)
{
ADAS1000_CsClr();
ADAS1000_Delay1us(1);
ADAS1000_Read(pDataBuffer, len);
ADAS1000_CsSet();
return true;
}
/***************************************************************************//**
* @brief Reads the specified number of frames.
*
* @param pDataBuffer - Buffer to store the read data.
* @param frameCnt - Number of frames to read.
* @param startRead - Set to 1 if a the frames read sequence must be started.
* @param stopRead - Set to 1 if a the frames read sequence must be sopped
* when exiting the function.
* @param waitForReady - Set to 1 if the function must wait for the READY bit
* to be set in the header.
* @param readyRepeat - Set to 1 if the device was configured to repeat the
* header until the READY bit is set.
*
* @return None.
*******************************************************************************/
void ADAS1000_ReadData(unsigned char* pDataBuffer, unsigned long frameCnt,
unsigned char startRead, unsigned char stopRead,
unsigned char waitForReady, unsigned char readyRepeat)
{
unsigned char readCmd[4] = {0, 0, 0, 0};
unsigned long ready = 0;
// If the read sequence must be started send a FRAMES command.
if(startRead)
{
readCmd[0] = ADAS1000_FRAMES; // Register address.
ADAS1000_Write(readCmd, 4);
}
// Read the number of requested frames.
while(frameCnt)
{
// If waiting for the READY bit to be set read the header until the bit is set, otherwise just read the entire frame.
if(waitForReady)
{
ready = 1;
while(ready == 1)
{
//if the header is repeated until the READY bit is set read only the header, otherwise read the entire frame
if(readyRepeat)
{
ADAS1000_Read(pDataBuffer, 4);
ready = *pDataBuffer & 0x40;
if(ready == 0)
{
ADAS1000_Read(pDataBuffer + 4, frameSize - 4);
pDataBuffer += frameSize;
frameCnt--;
}
}
else
{
ADAS1000_Read(pDataBuffer, frameSize);
ready = *pDataBuffer & 0x40;
if(ready == 0)
{
pDataBuffer += frameSize;
frameCnt--;
}
}
}
}
else
{
ADAS1000_Read(pDataBuffer, frameSize);
pDataBuffer += frameSize;
frameCnt--;
}
}
// If the frames read sequence must be stopped read a register to stop the frames read.
if(stopRead)
{
ADAS1000_GetRegisterValue(ADAS1000_FRMCTL, &ready);
}
}
/***************************************************************************//**
* @brief Computes the CRC for a frame.
*
* @param pBuf - Buffer holding the frame data.
*
* @return Returns the CRC value for the given frame.
*******************************************************************************/
unsigned long ADAS1000_ComputeFrameCrc(unsigned char *pBuf, int len)
{
unsigned char i = 0;
unsigned long crc = 0x00FFFFFF;
unsigned long poly = 0;
unsigned char bitCnt = 0;
unsigned long frmSize = 0;
// Select the CRC poly and word size based on the frame rate.
poly = CRC_POLY_2KHZ_16KHZ;
bitCnt = 24;
frmSize = len;
// Compute the CRC.
while(frmSize--)
{
crc ^= (((unsigned long)*pBuf++) << (bitCnt - 8));
for(i = 0; i < 8; i++)
{
if(crc & (1ul << (bitCnt - 1)))
crc = (crc << 1) ^ poly;
else
crc <<= 1;
}
}
return crc;
}
bool CheckADAS1000CRC16(unsigned char * Buffer,unsigned int BufSizeBytes)
{
/* Calculate 16-bit CRC */
unsigned int Calc_CRC = ADAS1000_ComputeFrameCrc(Buffer,BufSizeBytes);
/* Compare the calculated CRC with the 16-bit CRC constant */
bool Result = (Calc_CRC == CRC_CHECK_CONST_2KHZ_16KHZ);
/* Return CRC Check Pass or Fail */
return Result;
}
#include "ADAS1000Port.h"
bool ADAS1000_Write(unsigned char *pSendBuffer,unsigned long length)
{
return SPI2_Write(pSendBuffer, length);
}
bool ADAS1000_Read(unsigned char *pRecvBuff,unsigned long length)
{
return SPI2_Read(pRecvBuff, length);
}
bool ADAS1000_ReadWriteNBytes(unsigned char *pSendBuffer,unsigned char *pRecvBuff,unsigned long length)
{
return SPI2_ReadWriteNBytes(pSendBuffer, pRecvBuff, length);
}
void ADAS1000_PDClr(void)
{
DigitalIO_SetRealOutPutN(ADAS1000_MASTER_PD_PIN, false);
}
void ADAS1000_PDSet(void)
{
DigitalIO_SetRealOutPutN(ADAS1000_MASTER_PD_PIN, true);
}
void ADAS1000_CsClr(void)
{
DigitalIO_SetRealOutPutN(ADAS1000_MASTER_CS_PIN, false);
}
void ADAS1000_CsSet(void)
{
DigitalIO_SetRealOutPutN(ADAS1000_MASTER_CS_PIN, true);
}
void ADAS1000_RstClr(void)
{
DigitalIO_SetRealOutPutN(ADAS1000_MASTER_RESET_PIN, false);
}
void ADAS1000_RstSet(void)
{
DigitalIO_SetRealOutPutN(ADAS1000_MASTER_RESET_PIN, true);
}
bool ADAS1000_DrdyGet(void)
{
return DigitalIO_GetRealIutPutN(ADAS1000_MASTER_DRDY_PIN);
}
void ADAS1000_Delay1us(uint32_t time)
{
if (time < 1000)
{
delay_us(time);
}
else
{
delay_ms(time/1000);
}
}
uint32_t ADAS1000_MCUResourceInit(void)
{
ADAS1000_PDSet();
delay_ms(1);
ADAS1000_CsSet();
ADAS1000_RstSet();
delay_ms(10);
return 0;
}
The two header files are as follows:
#ifndef _ADAS1000_H_
#define _ADAS1000_H_
#include "ADAS1000Port.h"
/******************************************************************************/
/* ADAS1000 SPI Registers Memory Map */
/******************************************************************************/
#define ADAS1000_NOP 0x00 /* NOP (No operation) */
#define ADAS1000_ECGCTL 0x01 /* ECG Setting Register */
#define ADAS1000_LOFFCTL 0x02 /* Leads off Control Register */
#define ADAS1000_RESPCTL 0x03 /* Respiration Control Register */
#define ADAS1000_PACECTL 0x04 /* Pace Detection Control Register */
#define ADAS1000_CMREFCTL 0x05 /* Common Mode Reference and Shield Drive Control Register */
#define ADAS1000_GPIOCTL 0x06 /* GPIO Control Register */
#define ADAS1000_PACEAMPTH 0x07 /* Pace Amplitude Threshold2 */
#define ADAS1000_TESTTONE 0x08 /* Test Tone */
#define ADAS1000_CALDAC 0x09 /* Calibration DAC */
#define ADAS1000_FRMCTL 0x0A /* Frame Control Register */
#define ADAS1000_FILTCTL 0x0B /* Filter Control Register */
#define ADAS1000_LOFFUTH 0x0C /* Leads off Upper Threshold */
#define ADAS1000_LOFFLTH 0x0D /* Leads off Lower Threshold */
#define ADAS1000_PACEEDGETH 0x0E /* Pace Edge Threshold */
#define ADAS1000_PACELVLTH 0x0F /* Pace Level Threshold */
#define ADAS1000_LADATA 0x11 /* LA or LEAD I Data */
#define ADAS1000_LLDATA 0x12 /* LL or LEAD II Data */
#define ADAS1000_RADATA 0x13 /* RA or LEAD III Data */
#define ADAS1000_V1DATA 0x14 /* V1 or V1?Data */
#define ADAS1000_V2DATA 0x15 /* V2 or V2?Data */
#define ADAS1000_PACEDATA 0x1A /* Read Pace Detection Data */
#define ADAS1000_RESPMAG 0x1B /* Read Respiration Data Magnitude */
#define ADAS1000_RESPPH 0x1C /* Read Respiration Data Phase */
#define ADAS1000_LOFF 0x1D /* Leads Off Status */
#define ADAS1000_DCLEADSOFF 0x1E /* DC Leads off Register */
#define ADAS1000_OPSTAT 0x1F /* Operating State Register */
#define ADAS1000_EXTENDSW 0x20 /* Extended Switch for respiration inputs */
#define ADAS1000_CALLA 0x21 /* User gain calibration LA */
#define ADAS1000_CALLL 0x22 /* User gain calibration LL */
#define ADAS1000_CALRA 0x23 /* User gain calibration RA */
#define ADAS1000_CALV1 0x24 /* User gain calibration V1 */
#define ADAS1000_CALV2 0x25 /* User gain calibration V2 */
#define ADAS1000_LOAMLA 0x31 /* Leads off Amplitude for LA */
#define ADAS1000_LOAMLL 0x32 /* Leads off Amplitude for LL */
#define ADAS1000_LOAMRA 0x33 /* Leads off Amplitude for RA */
#define ADAS1000_LOAMV1 0x34 /* Leads off Amplitude for V1 */
#define ADAS1000_LOAMV2 0x35 /* Leads off Amplitude for V2 */
#define ADAS1000_PACE1_DATA 0x3A /* Pace1 Width & Amplitude2 */
#define ADAS1000_PACE2_DATA 0x3B /* Pace2 Width & Amplitude2 */
#define ADAS1000_PACE3_DATA 0x3C /* Pace3 Width & Amplitude2 */
#define ADAS1000_FRAMES 0x40 /* Frame Header - Read Data Frames */
#define ADAS1000_CRC 0x41 /* Frame CRC */
/******************************************************************************/
/* ECG Setting Register */
/******************************************************************************/
#define ADAS1000_ECGCTL_LAEN (1ul << 23) /* ECG Channel Enable, shuts down power to the channel, the input is
now HiZ : 1 = enabled, 0 = disabled */
#define ADAS1000_ECGCTL_LLEN (1ul << 22) /* ECG Channel Enable, shuts down power to the channel, the input is
now HiZ : 1 = enabled, 0 = disabled */
#define ADAS1000_ECGCTL_RAEN (1ul << 21) /* ECG Channel Enable, shuts down power to the channel, the input is
now HiZ : 1 = enabled, 0 = disabled */
#define ADAS1000_ECGCTL_V1EN (1ul << 20) /* ECG Channel Enable, shuts down power to the channel, the input is
now HiZ : 1 = enabled, 0 = disabled */
#define ADAS1000_ECGCTL_V2EN (1ul << 19) /* ECG Channel Enable, shuts down power to the channel, the input is
now HiZ : 1 = enabled, 0 = disabled */
#define ADAS1000_ECGCTL_CHCONFIG (1ul << 10) /* Setting this bit selects the differential AFE input:
0 = Single Ended Input ?Digital Lead Mode or Electrode Mode,
1 = Differential Input ?Analog Lead Mode */
#define ADAS1000_ECGCTL_GAIN (1ul << 8) /* Pre-Amp & Anti-Aliasing Filter Overall Gain:
00 = GAIN 0 = x1.4, 01 = GAIN 1 = x2.1,
10 = GAIN 2 = x2.8, 11 = GAIN 3 = x4.2 */
#define ADAS1000_ECGCTL_VREFBUF (1ul << 7) /* VREF Buffer Enable: 0 = Disabled, 1 = Enabled (when using internal
VREF, the VREFBUF must be enabled) */
#define ADAS1000_ECGCTL_CLKEXT (1ul << 6) /* Use external clock instead of crystal oscillator. The crystal oscillator
is automatically disabled if configured as SLAVE in Gang mode and the
Slave device should receive the CLK from the Master device:
0 = XTAL is CLK source, 1 = CLK_IO is CLK source. */
#define ADAS1000_ECGCTL_MASTER (1ul << 5) /* In gang mode, this bit selects the master (SYNC_GANG pin is configured
as an output). When in Single Channel Mode (GANG = 0), this bit is ignored:
0 = Slave, 1 = Master */
#define ADAS1000_ECGCTL_GANG (1ul << 4) /* Enable gang mode. Setting this bit causes the CLK_IO and to be activated:
0 = Single Channel mode, 1 = Gang Mode */
#define ADAS1000_ECGCTL_HP (1ul << 3) /* Selects the noise/power performance, this bit controls the ADC sampling
frequency. See specifications for further details:
0 = 1MSPS - low power, 1 = 2 MSPS ?High performance/low noise */
#define ADAS1000_ECGCTL_CNVEN (1ul << 2) /* Convert Enable ?Setting this bit enables the ADC conversion and filters:
0 = Idle, 1 = Conversion Enable */
#define ADAS1000_ECGCTL_PWREN (1ul << 1) /* Power Enable ?clearing this bit powers down the device. All analog blocks
are powered down and the external crystal is disabled:
0 = Power Down, 1 = Power Enable */
#define ADAS1000_ECGCTL_SWRST (1ul << 0) /* Software Reset ?setting this bit clears all registers to their reset value.
This bit automatically clears itself. The software reset requires a NOP
command to complete the reset: 0 = NOP, 1 = Reset */
/******************************************************************************/
/* Leads off Control Register */
/******************************************************************************/
#define ADAS1000_LOFFCTL_LAPH (1ul << 23) /* AC Leads Off Phase: 0 = in phase, 1 = 180?out of phase */
#define ADAS1000_LOFFCTL_LLPH (1ul << 22) /* AC Leads Off Phase: 0 = in phase, 1 = 180?out of phase */
#define ADAS1000_LOFFCTL_RAPH (1ul << 21) /* AC Leads Off Phase: 0 = in phase, 1 = 180?out of phase */
#define ADAS1000_LOFFCTL_V1PH (1ul << 20) /* AC Leads Off Phase: 0 = in phase, 1 = 180?out of phase */
#define ADAS1000_LOFFCTL_V2PH (1ul << 19) /* AC Leads Off Phase: 0 = in phase, 1 = 180?out of phase */
#define ADAS1000_LOFFCTL_CEPH (1ul << 18) /* AC Leads Off Phase: 0 = in phase, 1 = 180?out of phase */
#define ADAS1000_LOFFCTL_LAACLOEN (1ul << 17) /* Individual electrode AC Leads off enable. AC Leads off enables are
the OR of ACSEL and the individual AC Leads off Channel enables.
0 = AC Leads off disabled, 1 = AC Leads off enabled */
#define ADAS1000_LOFFCTL_LLACLOEN (1ul << 16) /* Individual electrode AC Leads off enable. AC Leads off enables are
the OR of ACSEL and the individual AC Leads off Channel enables.
0 = AC Leads off disabled, 1 = AC Leads off enabled */
#define ADAS1000_LOFFCTL_RAACLOEN (1ul << 15) /* Individual electrode AC Leads off enable. AC Leads off enables are
the OR of ACSEL and the individual AC Leads off Channel enables.
0 = AC Leads off disabled, 1 = AC Leads off enabled */
#define ADAS1000_LOFFCTL_V1ACLOEN (1ul << 14) /* Individual electrode AC Leads off enable. AC Leads off enables are
the OR of ACSEL and the individual AC Leads off Channel enables.
0 = AC Leads off disabled, 1 = AC Leads off enabled */
#define ADAS1000_LOFFCTL_V2ACLOEN (1ul << 13) /* Individual electrode AC Leads off enable. AC Leads off enables are
the OR of ACSEL and the individual AC Leads off Channel enables.
0 = AC Leads off disabled, 1 = AC Leads off enabled */
#define ADAS1000_LOFFCTL_CEACLOEN (1ul << 12) /* Individual electrode AC Leads off enable. AC Leads off enables are
the OR of ACSEL and the individual AC Leads off Channel enables.
0 = AC Leads off disabled, 1 = AC Leads off enabled */
#define ADAS1000_LOFFCTL_ACCURREN (1ul << 7) /* Set Current level for AC leads off (only active for ACSEL = 1).
00 = 12.5nA rms, 01 = 25nA rms,
10 = 50nA rms, 11 = 100nA rms */
#define ADAS1000_LOFFCTL_DCCURRENT (1ul << 2) /* Set Current level for DC leads off (only active for ACSEL = 0)
000 = 0nA, 001 = 10nA, 010 = 20nA, 011 = 30nA,
100 = 40nA, 101 = 50nA, 110 = 60nA, 111 = 70nA */
#define ADAS1000_LOFFCTL_ACSEL (1ul << 1) /* DC or AC (out of band) Leads Off Detection. If LOFFEN = 0, this bit
is don抰 care. If LOFFEN = 1, 0 = DC Leads Off Detection enabled.
(Individual AC leads off may be enabled through bits 12-17),
1 = DC Leads off Detection disabled. AC Leads Off Detection enabled
(all electrodes except CE electrode). */
#define ADAS1000_LOFFCTL_LOFFEN (1ul << 0) /* Enable Leads Off Detection:
0 = Leads Off Disabled, 1 = Leads Off Enabled */
/******************************************************************************/
/* Respiration Control Register */
/******************************************************************************/
#define ADAS1000_RESPCTL_RESPEXTSYNC (1ul << 15) /* Set to one to enable the MSB of the respiration DAC to be driven
out onto the GPIO[3] pin. This 64kHz signal can be used to
synchronize an external generator to the respiration carrier.
0 = normal GPIO3 function, 1 = MSB of RESPDAC driven onto GPIO[3] */
#define ADAS1000_RESPCTL_RESPEXTAMP (1ul << 14) /* For use with external instrumentation amplifier with respiration
circuit. Bypasses the on chip amplifier stage and input directly
to the ADC. */
#define ADAS1000_RESPCTL_RESPOUT (1ul << 13) /* Selects external respiration drive output. RESPDAC_RA is
automatically selected when RESPCAP = 1, 0 = RESPDAC _LL, 1 = RESPDAC_LA */
#define ADAS1000_RESPCTL_RESPCAP (1ul << 12) /* Selects source of Respiration Capacitors.
0 = Use internal capacitors, 1 = Use external capacitors */
#define ADAS1000_RESPCTL_RESPGAIN (1ul << 8) /* Respiration Inamp Gain (saturates at 10):
0000 = x1 gain, 0001 = x2 gain, 0010 = x3 gain,
...
1000 = x9 gain, 1001 = x10 gain, 11xx = x10 gain */
#define ADAS1000_RESPCTL_RESPEXTSEL (1ul << 7) /* Selects between EXT_RESP _LA or EXT_RESP_LL paths. Only applies
if External Respiration is selected in RESPSEL. EXT_RESP_RA
automatically gets enabled. 0 = EXT_RESP_LL, 1 = EXT_RESP _LA */
#define ADAS1000_RESPCTL_RESPSEL (1ul << 5) /* Set Leads for Respiration Measurement:
00 = Lead I, 01 = Lead II,
10 = Lead III, 11 = External Respiration path */
#define ADAS1000_RESPCTL_RESPAMP (1ul << 3) /* Set the test tone amplitude for respiration:
00 = Amplitude/8, 01 = Amplitude/4,
10 = Amplitude/2, 11 = Amplitude */
#define ADAS1000_RESPCTL_RESPFREQ (1ul << 1) /* Set Frequency for Respiration:
00 = 56kHz, 01 = 54kHz, 10 = 52kHz, 11 = 50kHz */
#define ADAS1000_RESPCTL_RESPEN (1ul << 0) /* Enable Respiration:
0 = Respiration Disabled, 1 = Respiration Enabled */
#define ADAS1000_RESPCTL_RESPGAIN_MASK (0x0000000Ful << 8)
#define ADAS1000_RESPCTL_RESPSEL_MASK (0x00000003ul << 5)
/******************************************************************************/
/* Pace Detection Control Register */
/******************************************************************************/
#define ADAS1000_PACECTL_PACEFILTW (1ul << 11) /* Pace width Filter:
0 = Filter Disabled, 1 = Filter Enabled */
#define ADAS1000_PACECTL_PACETFILT2 (1ul << 10) /* Pace validation filter 2:
0 = Filter Disabled, 1 = Filter Enabled */
#define ADAS1000_PACECTL_PACETFILT1 (1ul << 9) /* Pace validation filter 2:
0 = Filter Disabled, 1 = Filter Enabled */
#define ADAS1000_PACECTL_PACE3SEL (1ul << 7) /* Set Lead for Pace Detection Measurement:
00 = Lead I, 01 = Lead II, 10 = Lead III, 11 = Lead aVF */
#define ADAS1000_PACECTL_PACE2SEL (1ul << 5) /* Set Lead for Pace Detection Measurement:
00 = Lead I, 01 = Lead II, 10 = Lead III, 11 = Lead aVF */
#define ADAS1000_PACECTL_PACE1SEL (1ul << 3) /* Set Lead for Pace Detection Measurement:
00 = Lead I, 01 = Lead II, 10 = Lead III, 11 = Lead aVF */
#define ADAS1000_PACECTL_PACE3EN (1ul << 2) /* Enable Pace Detection Algorithm:
0 = Pace Detection Disabled, 1 = Pace Detection Enabled */
#define ADAS1000_PACECTL_PACE2EN (1ul << 1) /* Enable Pace Detection Algorithm:
0 = Pace Detection Disabled, 1 = Pace Detection Enabled */
#define ADAS1000_PACECTL_PACE1EN (1ul << 0) /* Enable Pace Detection Algorithm:
0 = Pace Detection Disabled, 1 = Pace Detection Enabled */
#define ADAS1000_PACECTL_PACE3SEL_MASK (0x00000003ul << 7)
#define ADAS1000_PACECTL_PACE2SEL_MASK (0x00000003ul << 5)
#define ADAS1000_PACECTL_PACE1SEL_MASK (0x00000003ul << 3)
/******************************************************************************/
/* Common Mode Reference and Shield Drive Control Register */
/******************************************************************************/
#define ADAS1000_CMREFCTL_LACM (1ul << 23) /* Common Mode Electrode Select */
#define ADAS1000_CMREFCTL_LLCM (1ul << 22) /* Any combination of the 5 input electrodes can be used to create the */
#define ADAS1000_CMREFCTL_RACM (1ul << 21) /* Common Mode signal, or the Common Mode signal can be driven from the */
#define ADAS1000_CMREFCTL_V1CM (1ul << 20) /* internal reference. Bits 23:19 are ignored when bit 2 is selected. */
#define ADAS1000_CMREFCTL_V2CM (1ul << 19) /* The Common Mode is the average of the selected electrodes. When a */
/* single electrode is selected, the Common Mode is the signal level of */
/* that electrode alone. */
/* 0 = does not contribute to the common mode */
/* 1 = contributes to the common mode */
#define ADAS1000_CMREFCTL_LARLD (1ul << 14) /* RLD Summing Junction
0 = does not contribute to RLD input
1 = contributes to RLD input */
#define ADAS1000_CMREFCTL_LLRLD (1ul << 13) /* RLD Summing Junction
0 = does not contribute to RLD input
1 = contributes to RLD input */
#define ADAS1000_CMREFCTL_RARLD (1ul << 12) /* RLD Summing Junction
0 = does not contribute to RLD input
1 = contributes to RLD input */
#define ADAS1000_CMREFCTL_V1RLD (1ul << 11) /* RLD Summing Junction
0 = does not contribute to RLD input
1 = contributes to RLD input */
#define ADAS1000_CMREFCTL_V2RLD (1ul << 10) /* RLD Summing Junction
0 = does not contribute to RLD input
1 = contributes to RLD input */
#define ADAS1000_CMREFCTL_CERLD (1ul << 9) /* RLD Summing Junction
0 = does not contribute to RLD input
1 = contributes to RLD input */
#define ADAS1000_CMREFCTL_CEREFEN (1ul << 8) /* Common Electrode Reference
0 = Common Electrode disabled
1 = Common Electrode enabled */
#define ADAS1000_CMREFCTL_RLDSEL (1ul << 4) /* Select electrode for reference drive
0000 = RL, 0001 = LA, 0010 = LL,
0011 = RA, 0100 = V1, 0101 = V2,
0110 to 1111 = Reserved */
#define ADAS1000_CMREFCTL_DRVCM (1ul << 3) /* Common mode output ?when set, the internally derived common mode
signal is driven out the common mode pin. This bit has no effect
if external common mode is selected.
0 = common mode is not driven out
1 = common mode is driven out the external common mode pin */
#define ADAS1000_CMREFCTL_EXTCM (1ul << 2) /* Select the source of Common Mode
(use when operating multiple devices together)
0 = Internal Common Mode selected
1 = External Common Mode selected */
#define ADAS1000_CMREFCTL_RLD_EN (1ul << 1) /* Enable Right Leg Drive Reference Electrode
0 = Disabled
1 = Enabled */
#define ADAS1000_CMREFCTL_SHLDEN (1ul << 0) /* Enable Shield Drive
0 = Shield Drive Disabled
1 = Shield Drive Enabled */
#define ADAS1000_CMREFCTL_RLDSEL_MASK (0x0000000Ful << 4)
/******************************************************************************/
/* GPIO Control Register */
/******************************************************************************/
#define ADAS1000_GPIOCTL_SPIFW (1ul << 18) /* Frame secondary SPI words with chip select
0 = MCS asserted for entire frame
1 = MCS asserted for individual word */
#define ADAS1000_GPIOCTL_SPIEN (1ul << 16) /* Secondary SPI Enable (ADAS1000 and ADAS1000-2 only) (SPI interface
providing ECG data at 128kHz data rate for external digital pace
algorithm detection ?uses GPIO0, GPIO1, GPIO2 pins)
0 = Disabled
1 = Enabled. The individual control bits for GPIO0, GPIO1,
GPIO2 are ignored. GPIO3 is not affected by SPIEN */
#define ADAS1000_GPIOCTL_G3CTL (1ul << 14) /* State of GPIO<3>
00 = High Impedance, 01 = Input,
10 = Output, 11 = Open Drain */
#define ADAS1000_GPIOCTL_G3OUT (1ul << 13) /* Output Value to be written to GPIO<3> when pad is configured as an
output or open drain
0 = Low Value
1 = High Value */
#define ADAS1000_GPIOCTL_G3IN (1ul << 12) /* (Read Only) Input Value read from GPIO<3> when pad is configured
as an input
0 = Low Value
1 = High Value */
#define ADAS1000_GPIOCTL_G2CTL (1ul << 10) /* State of GPIO<2>
00 = High Impedance, 01 = Input,
10 = Output, 11 = Open Drain */
#define ADAS1000_GPIOCTL_G2OUT (1ul << 9) /* Output Value to be written to GPIO<2> when pad is configured as an
output or open drain
0 = Low Value
1 = High Value */
#define ADAS1000_GPIOCTL_G2IN (1ul << 8) /* (Read Only) Input Value read from GPIO<2> when pad is configured as
an input.
0 = Low Value
1 = High Value */
#define ADAS1000_GPIOCTL_G1CTL (1ul << 6) /* State of GPIO<1>
00 = High Impedance, 01 = Input,
10 = Output, 11 = Open Drain */
#define ADAS1000_GPIOCTL_G1OUT (1ul << 5) /* Output Value to be written to GPIO<1> when pad is configured as an
output or open drain.
0 = Low Value
1 = High Value */
#define ADAS1000_GPIOCTL_G1IN (1ul << 4) /* (Read Only) Input Value read from GPIO<1> when pad is configured as
an input.
0 = Low Value
1 = High Value */
#define ADAS1000_GPIOCTL_G0CTL (1ul << 2) /* State of GPIO<0>
00 = High Impedance, 01 = Input,
10 = Output, 11 = Open Drain */
#define ADAS1000_GPIOCTL_G0OUT (1ul << 1) /* Output Value to be written to GPIO<0> when pad is configured as an
output or open drain.
0 = Low Value
1 = High Value */
#define ADAS1000_GPIOCTL_G0IN (1ul << 0) /* (Read Only) Input Value read from GPIO<0> when pad is configured
as an input
0 = Low Value
1 = High Value */
#define ADAS1000_GPIOCTL_G3CTL_MASK (0x00000003ul << 14)
#define ADAS1000_GPIOCTL_G2CTL_MASK (0x00000003ul << 10)
#define ADAS1000_GPIOCTL_G1CTL_MASK (0x00000003ul << 6)
#define ADAS1000_GPIOCTL_G0CTL_MASK (0x00000003ul << 2)
/******************************************************************************/
/* Pace Amplitude Threshold2 Register */
/******************************************************************************/
#define ADAS1000_PACEAMPTH_PACE3AMPTH (1ul << 16) /* Pace Amplitude Thresold */
#define ADAS1000_PACEAMPTH_PACE2AMPTH (1ul << 8) /* Threshold = N ?VREF/GAIN/216 */
#define ADAS1000_PACEAMPTH_PACE1AMPTH (1ul << 0)
#define ADAS1000_PACEAMPTH_PACE3AMPTH_MASK (0x000000FFul << 16)
#define ADAS1000_PACEAMPTH_PACE2AMPTH_MASK (0x000000FFul << 8)
#define ADAS1000_PACEAMPTH_PACE1AMPTH_MASK (0x000000FFul << 0)
/******************************************************************************/
/* Test Tone Register */
/******************************************************************************/
#define ADAS1000_TESTTONE_TONLA (1ul << 23) /* Tone Select */
#define ADAS1000_TESTTONE_TONLL (1ul << 22) /* 0 = 1.3V VCM_REF */
#define ADAS1000_TESTTONE_TONRA (1ul << 21) /* 1 = 1mV sinewave or squarewave for toneint, no connect for tonext */
#define ADAS1000_TESTTONE_TONV1 (1ul << 20)
#define ADAS1000_TESTTONE_TONV2 (1ul << 19)
#define ADAS1000_TESTTONE_TONTYPE (1ul << 3) /* 00 = 10Hz Sine Wave
01 = 150Hz Sine Wave
1x = 1Hz 1mV Square Wave */
#define ADAS1000_TESTTONE_TONINT (1ul << 2) /* Test Tone Internal or External
0 = External Test Tone
1 = Internal Test Tone */
#define ADAS1000_TESTTONE_TONOUT (1ul << 1) /* Test Tone out Enable
0 = disconnects test tone from CAL_DAC_IO during internal mode only
1 = Connects CAL_DAC_IO to test tone during internal mode. */
#define ADAS1000_TESTTONE_TONEN (1ul << 0) /* Enables an internal test tone to drive entire signal chain, from
pre-amp to SPI interface. This tone comes from the CAL DAC and goes
to the pre-amps through the internal mux. When TONEN (CALDAC) is
enabled, AC Leads off is disabled.
0 = Disable the test tone
1 = Enable the CALDAC 1mV SineWave test tone (Cal Mode has priority) */
#define ADAS1000_TESTTONE_TONTYPE_MASK (0x00000003ul << 3)
/******************************************************************************/
/* Calibration DAC Register */
/******************************************************************************/
#define ADAS1000_CALDAC_CALCHPEN (1ul << 13) /* Calibration Chop Clock Enable. The CALDAC output can be chopped to
lower 1/f noise. Chopping is done at 256kHz.
0 = Disabled
1 = Enabled. */
#define ADAS1000_CALDAC_CALMODEEN (1ul << 12) /* Calibration Mode Enable
0 = Disable Calibration mode
1 = Enable Calibration mode ?connect CAL DAC_IO,
begin data acquisition on ECG channels. */
#define ADAS1000_CALDAC_CALINT (1ul << 11) /* Calibration Internal or External
0 = External Cal ?calibration to be performed externally by
looping CAL_DAC_IO around into ECG channels.
1 = Internal Cal ?disconnects external switches for all ECG
channels and connects CALDAC internally to all ECG channels. */
#define ADAS1000_CALDAC_CALDACEN (1ul << 10) /* Enable 10-bit calibration DAC for cal mode or external use.
0 = Disable CALDAC
1 = Enable CALDAC, if a master device and not in calibration
mode then also connects CAL_DAC out to its_IO pin for
external use, if in Slave mode, the CALDAC will disable to
allow master to drive CAL_DAC_IO pin. When CALDAC is enabled,
AC Leads off is disabled. */
#define ADAS1000_CALDAC_CALDATA (1ul << 0) /* Set the CAL DAC value */
#define ADAS1000_CALDAC_CALDATA_MASK (0x000003FFul << 0)
/******************************************************************************/
/* Frame Control Register */
/******************************************************************************/
#define ADAS1000_FRMCTL_LEAD_I_LADIS (1ul << 23) /* Include/Exclude word from ECG data frame, if electrode/lead is */
#define ADAS1000_FRMCTL_LEAD_II_LLDIS (1ul << 22) /* included in the data word and the electrode falls off, then the */
#define ADAS1000_FRMCTL_LEAD_III_RADIS (1ul << 21) /* data word will be undefined. */
#define ADAS1000_FRMCTL_V1DIS (1ul << 20) /* 0 = Included in Frame */
#define ADAS1000_FRMCTL_V2DIS (1ul << 19) /* 1 = Exclude from Frame */
#define ADAS1000_FRMCTL_PACEDIS (1ul << 14) /* Include/Exclude word from ECG data frame
0 = Included in Frame
1 = Exclude from Frame */
#define ADAS1000_FRMCTL_RESPMDIS (1ul << 13) /* Respiration Magnitude
0 = Included in Frame
1 = Exclude from Frame */
#define ADAS1000_FRMCTL_RESPPHDIS (1ul << 12) /* Respiration Phase
0 = Included in Frame
1 = Exclude from Frame */
#define ADAS1000_FRMCTL_LOFFDIS (1ul << 11) /* Leads Off Status
0 = Included in Frame
1 = Exclude from Frame */
#define ADAS1000_FRMCTL_GPIODIS (1ul << 10) /* GPIO Word disable
0 = Included in Frame
1 = Exclude from Frame */
#define ADAS1000_FRMCTL_CRCDIS (1ul << 9) /* CRC Word disable
0 = Included in Frame
1 = Exclude from Frame */
#define ADAS1000_FRMCTL_SIGNEDEN (1ul << 8) /* In a master device configured for Lead Mode, the ECG data will
be signed. When in slave mode (electrode format), the ECG data
format is unsigned. Use this bit when using multiple devices to
make the slave device signed data.
0 = unsigned data (default)
1 = signed data */
#define ADAS1000_FRMCTL_ADIS (1ul << 7) /* Automatically disable PACE, RESP, LOFF words if their flags are
not set in the header.
0 = fixed frame format
1 = auto disable words */
#define ADAS1000_FRMCTL_RDYRPT (1ul << 6) /* Ready Repeat ?if this bit is set and the frame header indicates
data is not ready, the frame header is continuously sent until
data is ready.
0 = always send entire frame
1 = repeat frame header until ready */
#define ADAS1000_FRMCTL_DATAFMT (1ul << 4) /* Sets the Output Data Format
0 = Lead/Vector Format
(only available in 2kHz & 16kHz data rates)
1 = Electrode Format */
#define ADAS1000_FRMCTL_SKIP (1ul << 2) /* Skip interval ?this field provides a way to decimate the data
00 = output every frame
01 = output every other frame
1x = output every 4th frame */
#define ADAS1000_FRMCTL_FRMRATE_2KHZ 0x00 /* Sets the Output Data Rate to 2 kHz */
#define ADAS1000_FRMCTL_FRMRATE_16KHZ 0x01 /* Sets the Output Data Rate to 16 kHz */
#define ADAS1000_FRMCTL_FRMRATE_128KHZ 0x10 /* Sets the Output Data Rate to 128 kHz */
#define ADAS1000_FRMCTL_FRMRATE_31_25HZ 0x11 /* Sets the Output Data Rate to 31.25 Hz */
#define ADAS1000_FRMCTL_WORD_MASK (ADAS1000_FRMCTL_LEAD_I_LADIS | \
ADAS1000_FRMCTL_LEAD_II_LLDIS | \
ADAS1000_FRMCTL_LEAD_III_RADIS | \
ADAS1000_FRMCTL_V1DIS | \
ADAS1000_FRMCTL_V2DIS | \
ADAS1000_FRMCTL_PACEDIS | \
ADAS1000_FRMCTL_RESPMDIS | \
ADAS1000_FRMCTL_RESPPHDIS | \
ADAS1000_FRMCTL_LOFFDIS | \
ADAS1000_FRMCTL_GPIODIS | \
ADAS1000_FRMCTL_CRCDIS)
#define ADAS1000_FRMCTL_SKIP_MASK (0x00000003ul << 2)
#define ADAS1000_FRMCTL_FRMRATE_MASK (0x00000003ul << 0)
/******************************************************************************/
/* Filter Control Register */
/******************************************************************************/
#define ADAS1000_FILTCTL_MN2K (1ul << 5) /* 2kHz notch bypass for SPI Master
0 = notch filter bypassed
1 = notch filter present */
#define ADAS1000_FILTCTL_N2KBP (1ul << 4) /* 2kHz notch bypass
0 = notch filter present
1 = notch filter bypassed */
#define ADAS1000_FILTCTL_LPF (1ul << 2) /* 00 = 40Hz
01 = 150Hz
10 = 250 Hz
11 = 450Hz */
#define ADAS1000_FILTCTL_LPF_MASK (0x00000003ul << 2)
/******************************************************************************/
/* Leads off Upper Threshold Register */
/******************************************************************************/
#define ADAS1000_LOFFUTH_ADCOVER (1ul << 16) /* ADC over range threshold. An ADC out-of-range error will be flagged
if the ADC output is greater than the over range threshold.
The over range threshold is offset from the maximum value.
Threshold = max_value ?ADCOVER*2^6
0000 = max value (disabled)
0001 = max_value ?64
0010 = max_value ?128
...
1111: max_value ?960 */
#define ADAS1000_LOFFUTH_LOFFUTH (1ul << 0) /* AC Leads off upper Threshold. Leads off will be detected if the DC
or AC output is = N * 2 * VREF/GAIN/2^16. 0 = 0V */
#define ADAS1000_LOFFUTH_ADCOVER_MASK (0x0000000Ful << 16)
#define ADAS1000_LOFFUTH_LOFFUTH_MASK (0x0000FFFFul << 0)
/******************************************************************************/
/* Leads off Lower Threshold Register */
/******************************************************************************/
#define ADAS1000_LOFFLTH_ADCUNDR (1ul << 16) /* ADC under range threshold. An ADC out-of-range error will be flagged
if the ADC output is less than the under range threshold.
Threshold = min_value + ADCUNDR?^6
0000 = min value (disabled)
0001 = min_value + 64
0010 = min _value + 128
...
1111: min _value + 960 */
#define ADAS1000_LOFFLTH_LOFFLTH (1ul << 0) /* AC Leads off lower Threshold. Leads off will be detected if the DC
or AC output is = N * 2 * VREF/GAIN/2^16. 0 = 0V */
#define ADAS1000_LOFFLTH_ADCUNDR_MASK (0x0000000Ful << 16)
#define ADAS1000_LOFFLTH_LOFFLTH_MASK (0x0000FFFFul << 0)
/******************************************************************************/
/* Pace Edge Threshold Register */
/******************************************************************************/
#define ADAS1000_PACEEDGETH_PACE3EDGTH (1ul << 16) /* Pace edge trigger threshold */
#define ADAS1000_PACEEDGETH_PACE2EDGTH (1ul << 8) /* 0 = PACEAMPTH/2 */
#define ADAS1000_PACEEDGETH_PACE1EDGTH (1ul << 0) /* 1 = VREF/GAIN/2^16 */
/* N = N * VREF/GAIN/2^16 */
#define ADAS1000_PACEEDGETH_PACE3EDGTH_MASK (0x000000FFul << 16)
#define ADAS1000_PACEEDGETH_PACE2EDGTH_MASK (0x000000FFul << 8)
#define ADAS1000_PACEEDGETH_PACE1EDGTH_MASK (0x000000FFul << 0)
/******************************************************************************/
/* Pace Level Threshold Register */
/******************************************************************************/
#define ADAS1000_PACELVLTH_PACE3LVLTH (1ul << 16) /* Pace level threshold. This is a signed value. */
#define ADAS1000_PACELVLTH_PACE2LVLTH (1ul << 8) /* -1 = 0xFFF = -VREF/GAIN/2^16 */
#define ADAS1000_PACELVLTH_PACE1LVLTH (1ul << 0) /* 0 = 0x0000 = 0V */
/* +1 = 0x001 = +VREF/GAIN/2^16 */
/* N = N * VREF/GAIN/2^16 */
#define ADAS1000_PACELVLTH_PACE3LVLTH_MASK (0x000000FFul << 16)
#define ADAS1000_PACELVLTH_PACE2LVLTH_MASK (0x000000FFul << 8)
#define ADAS1000_PACELVLTH_PACE1LVLTH_MASK (0x000000FFul << 0)
/***********************************************************************************/
/* LA or LEAD I, LL or LEAD II, RA or LEAD III, V1 or V1? V2 or V2?Data Register */
/***********************************************************************************/
#define ADAS1000_LADATA_ADDRESS (1ul << 24) /* 0x11 : LA or LEAD I
0x12 : LL or LEAD II
0x13 : RA or LEAD II
0x14 : V1 or V1?
0x15 : V2 or V2?*/
#define ADAS1000_LADATA_ECG_DATA (1ul << 0) /* Channel Data Value. Data left justified (MSB) irrespective of data
rate. In electrode format, the value is an unsigned integer.
In Vector format, the value is a signed 2抯 complement integer format.
Vector format had 2x range compared to electrode format since it can
swing from +VREF to 朧REF, therefore the LSB size is double.
Electrode Format
Min value (000...) = 0V
Max value (1111...) = VREF/GAIN
LSB = (VREF/GAIN)/2N
Lead/Vector Format
Min value (1000...) = -(VREF/GAIN)
Max value (0111...) = +VREF/GAIN
LSB = 2?VREF/GAIN)/2N
Where N = # of data bits, 16 for 128kHz data rate or 24 for
2kHz/16kHz data rate. */
#define ADAS1000_LADATA_ADDRESS_MASK (0x000000FFul << 24)
#define ADAS1000_LADATA_ECG_DATA_MASK (0x00FFFFFFul << 0)
/******************************************************************************/
/* Read Pace Detection Data Register */
/******************************************************************************/
#define ADAS1000_PACEDATA_ADDRESS (1ul << 24) /* 0001 1010 = Pace Detection */
#define ADAS1000_PACEDATA_PACE3_DETECTED (1ul << 23) /* Pace 3 detected. This bit will be set once a pace pulse is
detected. This bit is set on the trailing edge of the pace pulse.
0 = Pace pulse not detected in current frame
1 = Pace pulse detected in this frame */
#define ADAS1000_PACEDATA_PACE_CH3_WIDTH (1ul << 20) /* This is log2(Width)-1 of the pace pulse.
N: Width = 2^(N+1) / 128kHz */
#define ADAS1000_PACEDATA_PACE_CH3_HEIGHT (1ul << 16) /* This is the log2(height) of the pace pulse
N: height = 2^N * VREF / GAIN / 2^16 */
#define ADAS1000_PACEDATA_PACE2_DETECTED (1ul << 15) /* Pace 2 detected. This bit will be set once a pace pulse is
detected. This bit is set on the trailing edge of the pace pulse.
0 = Pace pulse not detected in current frame
1 = Pace pulse detected in this frame*/
#define ADAS1000_PACEDATA_PACE_CH2_WIDTH (1ul << 12) /* This is log2(Width)-1 of the pace pulse.
N: Width = 2^(N+1) / 128kHz */
#define ADAS1000_PACEDATA_PACE_CH2_HEIGHT (1ul << 8) /* This is the log2(height) of the pace pulse
N: height = 2^N * VREF / GAIN / 2^16 */
#define ADAS1000_PACEDATA_PACE1_DETECTED (1ul << 7) /* Pace 1 detected. This bit will be set once a pace pulse is
detected. This bit is set on the trailing edge of the pace pulse.
0 = Pace pulse not detected in current frame
1 = Pace pulse detected in this frame */
#define ADAS1000_PACEDATA_PACE_CH1_WIDTH (1ul << 4) /* "This is log2(Width)-1 of the pace pulse.
N: Width = 2^(N+1) / 128kHz */
#define ADAS1000_PACEDATA_CH1_HEIGHT (1ul << 0) /* This is the log2(height) of the pace pulse
N: height = 2^N * VREF / GAIN / 2^16 */
#define ADAS1000_PACEDATA_ADDRESS_MASK (0x000000FFul << 24)
#define ADAS1000_PACEDATA_PACE_CH3_WIDTH_MASK (0x00000007ul << 20)
#define ADAS1000_PACEDATA_PACE_CH3_HEIGHT_MASK (0x0000000Ful << 16)
#define ADAS1000_PACEDATA_PACE_CH2_WIDTH_MASK (0x00000007ul << 12)
#define ADAS1000_PACEDATA_PACE_CH2_HEIGHT_MASK (0x0000000Ful << 8)
#define ADAS1000_PACEDATA_PACE_CH1_WIDTH_MASK (0x00000007ul << 4)
#define ADAS1000_PACEDATA_PACE_CH1_HEIGHT_MASK (0x0000000Ful << 0)
/******************************************************************************/
/* Read Respiration Data Magnitude Register */
/******************************************************************************/
#define ADAS1000_RESPMAG_ADDRESS (1ul << 24) /* 0001 1011 = Respiration Magnitude */
#define ADAS1000_RESPMAG_MAGNITUDE (1ul << 0) /* Magnitude of respiration signal. This is an unsigned value. */
#define ADAS1000_RESPMAG_ADDRESS_MASK (0x000000FFul << 24)
#define ADAS1000_RESPMAG_MAGNITUDE_MASK (0x00FFFFFFul << 0)
/******************************************************************************/
/* Read Respiration Data Phase Register */
/******************************************************************************/
#define ADAS1000_RESPPH_ADDRESS (1ul << 24) /* 0001 1100 = Respiration Phase */
#define ADAS1000_RESPPH_PHASE (1ul << 0) /* Phase of respiration signal. Can be interpreted as either signed or
unsigned value. If unsigned, the range is from 0 to 2pi. If as a
signed value, the range is from 杙i to +pi.
0x000000 = 0
0x000001 = 2pi / 2^24
0x400000 = pi/2
0x800000 = +pi = -pi
0xC00000 = +3pi/2 = -pi/2
0xFFFFFF = +2pi(1 - 2^(-24)) = -2p / 2^24 */
#define ADAS1000_RESPPH_ADDRESS_MASK (0x000000FFul << 24)
#define ADAS1000_RESPPH_PHASE_MASK (0x00FFFFFFul << 0)
/******************************************************************************/
/* Leads Off Status Register */
/******************************************************************************/
#define ADAS1000_LOFF_ADDRESS (1ul << 24) /* Address bits define the word data 0001 1101 = Leads Off */
#define ADAS1000_LOFF_RL_LEADS_OFF_STATUS (1ul << 23) /* Electrode Connection Status. If either DC or AC leads off */
#define ADAS1000_LOFF_LA_LEADS_OFF_STATUS (1ul << 22) /* is enabled, these bits are the corresponding leads off status. */
#define ADAS1000_LOFF_LL_LEADS_OFF_STATUS (1ul << 21) /* If both DC and AC leads off are enabled, these bits reflect */
#define ADAS1000_LOFF_RA_LEADS_OFF_STATUS (1ul << 20) /* only the AC leads off status. DC leads off is available in */
#define ADAS1000_LOFF_V1_LEADS_OFF_STATUS (1ul << 19) /* the DCLEADSOFF register. The common electrodes only have DC */
#define ADAS1000_LOFF_V2_LEADS_OFF_STATUS (1ul << 18) /* leads off detection. An AC leads off signal can be injected */
#define ADAS1000_LOFF_CELO (1ul << 13) /* into the common electrode, but there is no ADC input to measure */
/* its amplitude. If the common electrode is off, it will affect */
/* the AC leads off amplitude of the other electrodes. These bits */
/* accumulate in the frame buffer and are cleared when the frame */
/* buffer is loaded into the SPI buffer. */
/* 0 = Electrode is connected */
/* 1 = Electrode is disconnected */
#define ADAS1000_LOFF_LAADCOR (1ul << 12) /* ADC out of range error. */
#define ADAS1000_LOFF_LLADCOR (1ul << 11) /* These status bits indicate the resulting ADC code is out of */
#define ADAS1000_LOFF_RAADCOR (1ul << 10) /* range. These bits accumulate in the frame buffer and are */
#define ADAS1000_LOFF_V1ADCOR (1ul << 9) /* cleared when the frame buffer is loaded into the SPI buffer. */
#define ADAS1000_LOFF_V2ADCOR (1ul << 8)
#define ADAS1000_LOFF_ADDRESS_MASK (0x000000FFul << 24)
/******************************************************************************/
/* DC Leads off Register */
/******************************************************************************/
#define ADAS1000_DCLEADSOFF_ADDRESS (1ul << 24) /* Address bits define the word data 0001 1110 = DC Leads Off */
#define ADAS1000_DCLEADSOFF_RL_INPUT_OVERRANGE (1ul << 23) /* The DC leads off detection is comparator based and compares */
#define ADAS1000_DCLEADSOFF_LA_INPUT_OVERRANGE (1ul << 22) /* to a fixed level. Per electrode bits flag if the DC leads off */
#define ADAS1000_DCLEADSOFF_LL_INPUT_OVERRANGE (1ul << 21) /* comparator threshold level has been exceeded. */
#define ADAS1000_DCLEADSOFF_RA_INPUT_OVERRANGE (1ul << 20) /* 0 = electrode < overrange threshold, 2.4 V */
#define ADAS1000_DCLEADSOFF_CE_INPUT_OVERRANGE (1ul << 13) /* 1 = electrode > overrange threshold, 2.4 V */
#define ADAS1000_DCLEADSOFF_RL_INPUT_UNDERRANGE (1ul << 12) /* The DC leads off detection is comparator based and compares */
#define ADAS1000_DCLEADSOFF_LA_INPUT_UNDERRANGE (1ul << 11) /* to a fixed level. Per electrode bits flag if the DC leads off */
#define ADAS1000_DCLEADSOFF_LL_INPUT_UNDERRANGE (1ul << 10) /* comparator threshold level has been exceeded. */
#define ADAS1000_DCLEADSOFF_RA_INPUT_UNDERRANGE (1ul << 9) /* 0 = electrode > underrange threshold, 0.2 V */
#define ADAS1000_DCLEADSOFF_CE_INPUT_UNDERRANGE (1ul << 2) /* 1 = electrode < underrange threshold, 0.2 V */
#define ADAS1000_DCLEADSOFF_ADDRESS_MASK (0x000000FFul << 24)
/******************************************************************************/
/* Extended Switch for Respiration Inputs Register */
/******************************************************************************/
#define ADAS1000_EXTENDSW_EXTRESP_RA_LA (1ul << 23) /* External Respiration electrode input switch to channel */
#define ADAS1000_EXTENDSW_EXTRESP_RA_LL (1ul << 22) /* electrode input. */
#define ADAS1000_EXTENDSW_EXTRESP_RA_RA (1ul << 21) /* 0 = switch open */
#define ADAS1000_EXTENDSW_EXTRESP_RA_V1 (1ul << 20) /* 1 = switch closed */
#define ADAS1000_EXTENDSW_EXTRESP_RA_V2 (1ul << 19)
#define ADAS1000_EXTENDSW_EXTRESP_LL_LA (1ul << 18)
#define ADAS1000_EXTENDSW_EXTRESP_LL_LL (1ul << 17)
#define ADAS1000_EXTENDSW_EXTRESP_LL_RA (1ul << 16)
#define ADAS1000_EXTENDSW_EXTRESP_LL_V1 (1ul << 15)
#define ADAS1000_EXTENDSW_EXTRESP_LL_V2 (1ul << 14)
#define ADAS1000_EXTENDSW_EXTRESP_LA_LA (1ul << 13)
#define ADAS1000_EXTENDSW_EXTRESP_LA_LL (1ul << 12)
#define ADAS1000_EXTENDSW_EXTRESP_LA_RA (1ul << 11)
#define ADAS1000_EXTENDSW_EXTRESP_LA_V1 (1ul << 10)
#define ADAS1000_EXTENDSW_EXTRESP_LA_V2 (1ul << 9)
#define ADAS1000_EXTENDSW_FREE_V1 (1ul << 8) /* V1 and V2 electrodes may be used for measurement purposes */
#define ADAS1000_EXTENDSW_FREE_V2 (1ul << 7) /* other than ECG. To achieve this, they need to be disconnected */
/* from the patient VCM voltage provided from the internal common */
/* mode buffer and instead connected to the internal VCM_REF level */
/* of 1.3V. Set FREE_Vx bits high to connect negative input of V1 */
/* channel will be tied to internal VCM_REF level. This allows user*/
/* to make alternative measurements on V1 channel relative to the */
/* VCM_REF level. If using Digital lead mode, uses these bits in */
/* conjunction with NO_MATH_Vx bits [6:5]. */
#define ADAS1000_EXTENDSW_NOMATH_V1 (1ul << 6) /* In Digital Lead Mode, the digital core calculates the math on V1 */
#define ADAS1000_EXTENDSW_NOMATH_V2 (1ul << 5) /* and V2 with respect to WCT (LA+LL+RA)/3 providing V1?and V2? */
/* Where V1 or V2 are used for measurement of something other than */
/* ECG, then the math calculation needs to be disabled. These bits */
/* are most likely used in conjunction with bits FREE_Vx [8:7]. */
/* Set NOMATH_Vx bits high to disable the math calculation in V1 */
/* and V2 respectively. */
/******************************************************************************/
/* User gain calibration LA, LL, RA, V1, V2 Register */
/******************************************************************************/
#define ADAS1000_CAL_ADDRESS (1ul << 24) /* 0x21 : CAL LA
0x22 : CAL LL
0x23 : CAL RA
0x24 : CAL V1
0x25 : CAL V2 */
#define ADAS1000_CAL_USRCAL (1ul << 23) /* User can choose between:
0 = default calibration values
1 = user calibration values */
#define ADAS1000_CAL_CALVALUE (1ul << 0) /* Gain Calibration value.
Result = data * (1 + GAIN * 2^(-17))
The value read from this register is the current gain calibration value.
If the USRCAL bit is clear, this register returns the default value for
the current gain setting.
0x7FF (+2047) = *1.00000011111111111b
0x001 (+1) = *1.00000000000000001b
0x000 (0) = *1.00000000000000000b
0xFFF (-1) = *0.11111111111111111b
0x800 (-2048) = *0.11111100000000000b */
#define ADAS1000_CAL_ADDRESS_MASK (0x000000FFul << 24)
#define ADAS1000_CAL_CALVALUE_MASK (0x00000FFFul << 0)
/******************************************************************************/
/* Leads off Amplitude for LA, LL, RA, V1, V2 Register */
/******************************************************************************/
#define ADAS1000_LOAM_ADDRESS (1ul << 24) /* 0x31 : LA AC Leads off Magnitude
0x32 : LL AC Leads off Magnitude
0x33 : RA AC Leads off Magnitude
0x34 : V1 AC Leads off Magnitude
0x35 : V2 AC Leads off Magnitude */
#define ADAS1000_LOAM_LOFFAM (1ul << 0) /* Measured Amplitude.
When AC leads off is selected, the data is the average of the rectified
2kHz bandpass filter with an update rate of 8Hz and cutoff frequency at
2Hz. The output is the amplitude of the 2kHz signal scaled by 2/pi
approximately = 0.6 (average of rectified sine wave). To convert to RMS,
scale the output by pi / (2*sqrt(2)).
Leads off (unsigned):
Min 0x0000 = 0V
LSB 0x0001= VREF / GAIN / 2^16
Max 0xFFFF = VREF / GAIN */
#define ADAS1000_LOAM_ADDRESS_MASK (0x000000FFul << 24)
#define ADAS1000_LOAM_LOFFAM_MASK (0x0000FFFFul << 0)
/******************************************************************************/
/* Pace1, Pace2, Pace3 Width & Amplitude2 Register */
/******************************************************************************/
#define ADAS1000_PACE_DATA_ADDRESS (1ul << 24) /* 0x3A : PACE1DATA
0x3B : PACE2DATA
0x3C : PACE3DATA */
#define ADAS1000_PACE_DATA_HEIGHT (1ul << 8) /* Measured pace height in signed 2抯 complement value
0 = 0
1 = VREF / GAIN / 2^16
N = N * VREF / GAIN / 2^16 */
#define ADAS1000_PACE_DATA_WIDTH (1ul << 0) /* Measured pace width in 128kHz samples
N: N / 128kHz = width
12: 12 / 128kHz = 93us
255: 255 / 128kHz = 2.0ms */
#define ADAS1000_PACE_DATA_ADDRESS_MASK (0x000000FFul << 24)
#define ADAS1000_PACE_DATA_HEIGHT_MASK (0x0000FFFFul << 8)
#define ADAS1000_PACE_DATA_WIDTH_MASK (0x000000FFul << 0)
/******************************************************************************/
/* Frame Header - Read Data Frames Register */
/******************************************************************************/
#define ADAS1000_FRAMES_MARKER (1ul << 31) /* Header marker, set to ??for the header */
#define ADAS1000_FRAMES_READY_BIT (1ul << 30) /* Ready bit indicates if ECG frame data is calculated and
ready for reading.
0 = Ready, data frame follows
1 = Busy */
#define ADAS1000_FRAMES_OVERFLOW (1ul << 28) /* Overflow bits indicate that since the last frame read,
a number of frames have been missed. This field saturates
at the maximum count. The data in the frame including
this header word is valid but old if the overflow bits
are > 0. When using Skip mode (FRMCTL register (0x0A)[3:2]),
the Overflow bit acts as a flag, where a non-zero value
indicates an overflow.
00 = 0 missed
01 = 1 frame missed
10 = 2 frames missed
11 = 3 or more frames missed */
#define ADAS1000_FRAMES_FAULT (1ul << 27) /* Internal device error detected.
0 = normal operation
1 = error condition */
#define ADAS1000_FRAMES_PACE3_DETECTED (1ul << 26) /* PACE 3 Indicates Pacing Artifact was qualified at most
recent point.
0 = No Pacing Artifact
1 = Pacing Artifact Present */
#define ADAS1000_FRAMES_PACE2_DETECTED (1ul << 25) /* PACE 2 Indicates Pacing Artifact was qualified at most
recent point.
0 = No Pacing Artifact
1 = Pacing Artifact Present */
#define ADAS1000_FRAMES_PACE1_DETECTED (1ul << 24) /* PACE 1 Indicates Pacing Artifact was qualified at most
recent point.
0 = No Pacing Artifact
1 = Pacing Artifact Present */
#define ADAS1000_FRAMES_RESPIRATION (1ul << 23) /* 0 = no new respiration data
1 = respiration data updated */
#define ADAS1000_FRAMES_LEADS_OFF_DETECTED (1ul << 22) /* If both DC & AC leads off are enabled, this bit is the
OR of all the AC leads off detect flags. If only AC or
DC leads off is enabled (but not both, this bit reflects
the OR of all DC & AC leads off flags.
0 = all leads connected
1 = one or more leads off detected */
#define ADAS1000_FRAMES_DC_LEADS_OFF_DETECTED (1ul << 21) /* 0 = all leads connected
1 = one or more leads off detected */
#define ADAS1000_FRAMES_ADC_OUT_OF_RANGE (1ul << 20) /* 0 = ADC within range
1 = ADC out of range */
/******************************************************************************/
/* Frame CRC Register */
/******************************************************************************/
#define ADAS1000_CRC_MASK (0x00FFFFFF << 0) /* Cyclic Redundancy Check */
/******************************************************************************/
/* ADAS1000 data rates, word sizes and frame sizes */
/******************************************************************************/
#define ADAS1000_31_25HZ_FRAME_RATE 3125
#define ADAS1000_2KHZ_FRAME_RATE 2000
#define ADAS1000_16KHZ_FRAME_RATE 16000
#define ADAS1000_128KHZ_FRAME_RATE 128000
#define ADAS1000_31_25HZ_WORD_SIZE 32
#define ADAS1000_2KHZ_WORD_SIZE 32
#define ADAS1000_16KHZ_WORD_SIZE 32
#define ADAS1000_128KHZ_WORD_SIZE 16
#define ADAS1000_31_25HZ_FRAME_SIZE 12
#define ADAS1000_2KHZ_FRAME_SIZE 12
#define ADAS1000_16KHZ_FRAME_SIZE 12
#define ADAS1000_128KHZ_FRAME_SIZE 15
/******************************************************************************/
/* ADAS1000 CRC constants */
/******************************************************************************/
//#define CRC_POLY_2KHZ_16KHZ 0x005D6DCBul
//#define CRC_CHECK_CONST_2KHZ_16KHZ 0x0015A0BAul
//#define CRC_POLY_128KHZ 0x00001021ul
//#define CRC_CHECK_CONST_128KHz 0x00001D0Ful
#define CRC_POLY_2KHZ_16KHZ 0x015D6DCB
#define CRC_POLY_128KHZ 0x00011021
#define CRC_CHECK_CONST_2KHZ_16KHZ 0x0015A0BA
#define CRC_CHECK_CONST_128KHz 0x00001D0F
/******************************************************************************/
/* Functions Prototypes */
/******************************************************************************/
/* Initializes the communication with ADAS1000 and checks if the device is present.*/
extern bool ADAS1000_StartMea();
extern bool ADAS1000_StopMea();
/* Reads the value of a ADAS1000 register */
extern void ADAS1000_GetRegisterValue(unsigned char regAddress, unsigned long* regVal);
/* Writes a value into a ADAS1000 register */
extern void ADAS1000_SetRegisterValue(unsigned char regAddress, unsigned long regVal);
/* Performs a software reset of the ADAS1000 */
extern void ADAS1000_SoftwareReset(void);
extern void ADAS1000_HardwareReset(void);
/* Selects which words are not included in a data frame */
extern void ADAS1000_SetInactiveFrameWords(unsigned long wordsMask);
extern bool ADAS1000TestFirstComm();
/* Sets the frame rate */
extern bool ADAS1000_SetFrameRate(unsigned long rate, unsigned char skip);
extern bool ADAS1000_ReadHeader();
extern bool ADAS1000_ReadDataOne(unsigned char* pDataBuffer, int len);
/* Reads the specified number of frames */
extern void ADAS1000_ReadData(unsigned char* pDataBuffer, unsigned long frameCnt,
unsigned char startRead, unsigned char stopRead,
unsigned char waitForReady, unsigned char readyRepeat);
/* Computes the CRC for a frame */
extern unsigned long ADAS1000_ComputeFrameCrc(unsigned char *pBuf, int len);
extern bool CheckADAS1000CRC16(unsigned char * Buffer,unsigned int BufSizeBytes);
#endif /* _ADAS1000_H_ */
#ifndef _ADAS1000_PORT_H
#define _ADAS1000_PORT_H
#include "common.h"
#include "dio.h"
#include "delay.h"
#include "spi_adas1021.h"
extern bool ADAS1000_Write(unsigned char *pSendBuffer,unsigned long length);
extern bool ADAS1000_Read(unsigned char *pRecvBuff,unsigned long length);
extern bool ADAS1000_ReadWriteNBytes(unsigned char *pSendBuffer,unsigned char *pRecvBuff,unsigned long length);
extern void ADAS1000_PDClr(void);
extern void ADAS1000_PDSet(void);
extern void ADAS1000_CsClr(void);
extern void ADAS1000_CsSet(void);
extern void ADAS1000_RstClr(void);
extern void ADAS1000_RstSet(void);
extern bool ADAS1000_DrdyGet(void);
extern void ADAS1000_Delay1us(uint32_t time);
extern uint32_t ADAS1000_MCUResourceInit(void);
#endif
The main control part collects the data of ADAS1000 and forwards the port. Here is the source code of the serial port part. The other parts are relatively easy to implement:
#include "uart4_dma.h"
int g_txFlag = 0; //有数据发送
int g_rxFlag = 0; //有数据接受
int g_rxLedNum = 10; //短闪表示收到数据,30长闪表示数据通过CRC有效
static UART_HandleTypeDef m_UART4_Handler; //UART句柄
static DMA_HandleTypeDef m_UART4TxDMA_Handler; //DMA
static DMA_HandleTypeDef m_UART4RxDMA_Handler; //DMA
//数据缓冲区
static volatile u8 uart4_TxBuf[MAX_UART4_TX_BUFFER_SIZE] = {0};
static volatile u8 uart4_RxBuf[MAX_UART4_RX_BUFFER_SIZE] = {0};
//发送队列
static sRingBuffer uart4_TxQueue;
//接收队列
static sRingBuffer uart4_RxQueue;
static bool m_rx4FrameState = false;
//DMA缓冲
static volatile u8 m_dma_rx4_buff[UART4_DMA_RX_BUFF_LEN] = {0};
#if !UART4_BULK_TX
static volatile u8 m_dma_tx4_buff[UART4_DMA_TX_BUFF_LEN] = {0};
#endif
//初始化循环队列
static void Uart4_Init_Queue()
{
InitQueue(&uart4_TxQueue, (void *)uart4_TxBuf, MAX_UART4_TX_BUFFER_SIZE);
InitQueue(&uart4_RxQueue, (void *)uart4_RxBuf, MAX_UART4_RX_BUFFER_SIZE);
}
//循环队列中循环写入一个数据
static void Uart4_Write_Queue(sRingBuffer *pQueue,u8 inData)
{
u8 *p = (u8 *)(pQueue->pData);
*(p+pQueue->wCounter) = inData;
WriteQueueLoopOffset(pQueue);
}
//循环队列循环写入批量数据
static void Uart4_Write_Queue_Bulk(sRingBuffer *pQueue,u8 *pData, int len)
{
int space = 0;
int rightSpace = 0;
if (len > pQueue->maxLen)
{
return;
}
u8 *p = (u8 *)(pQueue->pData);
//检查剩余空间,是否需要移动读指针
space = CalcNumSpace(pQueue);
if (len > space)
{
ReadQueueOffsetBulk(pQueue, len-space);
}
//检查右侧空间,是否需要分段复制
rightSpace = pQueue->maxLen - pQueue->wCounter;
if (len > rightSpace)
{
memcpy(p+pQueue->wCounter, pData, rightSpace);
memcpy(p, pData+rightSpace, len-rightSpace);
}
else
{
memcpy(p+pQueue->wCounter, pData, len);
}
WriteQueueOffsetBulk(pQueue, len);
return;
}
//从循环队列中读出一个数据
static bool Uart4_Read_Queue(sRingBuffer *pQueue, u8 *pOutData)
{
u8 *p = (u8 *)(pQueue->pData);
if (IsEmptyQueue(pQueue))
{
return false;
}
*pOutData = *(p+pQueue->rCounter);
*(p+pQueue->rCounter) = 0;
ReadQueueOffset(pQueue);
return true;
}
//循环队列中读出批量数据
static int Uart4_Read_Queue_Bulk(sRingBuffer *pQueue, u8 **ppDataAddress)
{
int len = 0;
int rightSpace = 0;
u8 *p = (u8 *)(pQueue->pData);
if (IsEmptyQueue(pQueue))
{
return 0;
}
*ppDataAddress = p+pQueue->rCounter;
rightSpace = pQueue->maxLen - pQueue->rCounter;
if (rightSpace > pQueue->len)
{
len = pQueue->len;
}
else
{
len = rightSpace;
}
ReadQueueOffsetBulk(pQueue, len);
return len;
}
void UART4_GPIO_Config()
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
//GPIO端口设置
GPIO_InitTypeDef GPIO_Initure;
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_UART4;
PeriphClkInitStruct.Uart4ClockSelection = RCC_UART4CLKSOURCE_PCLK1;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_UART4_CLK_ENABLE(); //使能USART4时钟, APB1 54M
GPIO_Initure.Pin=GPIO_PIN_11 | GPIO_PIN_12; //RX:PA11 TX:PA12
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_NOPULL; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速
GPIO_Initure.Alternate=GPIO_AF6_UART4; //复用为USART4
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化
}
static bool UART4_RX_DMA_Config()
{
__HAL_RCC_DMA1_CLK_ENABLE();//
__HAL_LINKDMA(&m_UART4_Handler,hdmarx,m_UART4RxDMA_Handler); //
//Rx DMA配置
m_UART4RxDMA_Handler.Instance=DMA1_Stream2; //数据流选择
m_UART4RxDMA_Handler.Init.Channel=DMA_CHANNEL_4; //通道选择
m_UART4RxDMA_Handler.Init.Direction=DMA_PERIPH_TO_MEMORY; //内存到外设
m_UART4RxDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE; //外设非增量模式
m_UART4RxDMA_Handler.Init.MemInc=DMA_MINC_ENABLE; //内存增量模式
m_UART4RxDMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; //外设数据长度1byte
m_UART4RxDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据长度1byte
m_UART4RxDMA_Handler.Init.Mode=DMA_NORMAL; //普通模式
m_UART4RxDMA_Handler.Init.Priority=DMA_PRIORITY_VERY_HIGH; //中等优先级
m_UART4RxDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE; //
m_UART4RxDMA_Handler.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL; //
m_UART4RxDMA_Handler.Init.MemBurst=DMA_MBURST_SINGLE; //内存单次突发传输
m_UART4RxDMA_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE; //外设单次突发传输
HAL_DMA_DeInit(&m_UART4RxDMA_Handler);
if (HAL_DMA_Init(&m_UART4RxDMA_Handler) != HAL_OK)
{
return false;
}
return true;
}
static bool UART4_TX_DMA_Config()
{
__HAL_RCC_DMA1_CLK_ENABLE();//
__HAL_LINKDMA(&m_UART4_Handler,hdmatx,m_UART4TxDMA_Handler); //
//Tx DMA配置
m_UART4TxDMA_Handler.Instance=DMA1_Stream4; //数据流选择
m_UART4TxDMA_Handler.Init.Channel=DMA_CHANNEL_4; //通道选择
m_UART4TxDMA_Handler.Init.Direction=DMA_MEMORY_TO_PERIPH; //内存到外设
m_UART4TxDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE; //外设非增量模式
m_UART4TxDMA_Handler.Init.MemInc=DMA_MINC_ENABLE; //内存增量模式
m_UART4TxDMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; //外设数据长度1byte
m_UART4TxDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据长度1byte
m_UART4TxDMA_Handler.Init.Mode=DMA_NORMAL; //普通模式
m_UART4TxDMA_Handler.Init.Priority=DMA_PRIORITY_MEDIUM; //中等优先级
m_UART4TxDMA_Handler.Init.FIFOMode=DMA_FIFOMODE_DISABLE; //
m_UART4TxDMA_Handler.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL; //
m_UART4TxDMA_Handler.Init.MemBurst=DMA_MBURST_SINGLE; //内存单次突发传输
m_UART4TxDMA_Handler.Init.PeriphBurst=DMA_PBURST_SINGLE; //外设单次突发传输
HAL_DMA_DeInit(&m_UART4TxDMA_Handler);
if (HAL_DMA_Init(&m_UART4TxDMA_Handler) != HAL_OK)
{
return false;
}
return true;
}
static bool UART4_Config(u32 baud)
{
//UART 初始化设置
m_UART4_Handler.Instance=UART4; //UART4
m_UART4_Handler.Init.BaudRate=baud; //波特率
m_UART4_Handler.Init.WordLength=UART_WORDLENGTH_8B; //字长为8位数据格式
m_UART4_Handler.Init.StopBits=UART_STOPBITS_1; //一个停止位
m_UART4_Handler.Init.Parity=UART_PARITY_NONE; //无奇偶校验位
m_UART4_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; //无硬件流控
m_UART4_Handler.Init.Mode=UART_MODE_TX_RX; //收发模式
m_UART4_Handler.Init.OverSampling = UART_OVERSAMPLING_16;
m_UART4_Handler.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
m_UART4_Handler.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&m_UART4_Handler) != HAL_OK)
{
return false;
}
return true;
}
//初始化IO 串口1
//baud:波特率
void uart4_init(u32 baud)
{
UART4_Config(baud);
UART4_RX_DMA_Config();
UART4_TX_DMA_Config();
Uart4_Init_Queue();
__HAL_UART_CLEAR_IT(&m_UART4_Handler,UART_IT_IDLE);
__HAL_UART_ENABLE_IT(&m_UART4_Handler,UART_IT_IDLE);
#if !UART4_RX_USE_DMA
__HAL_UART_CLEAR_IT(&m_UART4_Handler,UART_IT_RXNE);
__HAL_UART_ENABLE_IT(&m_UART4_Handler,UART_IT_RXNE);
HAL_UART_Receive_IT(&m_UART4_Handler, (u8 *)m_dma_rx4_buff, UART4_DMA_RX_BUFF_LEN);
#else
__HAL_DMA_CLEAR_FLAG(&m_UART4RxDMA_Handler, DMA_FLAG_TCIF1_5);
__HAL_DMA_ENABLE_IT(&m_UART4RxDMA_Handler, DMA_IT_TC);
HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);
HAL_NVIC_SetPriority(DMA1_Stream2_IRQn,0,2);
HAL_UART_Receive_DMA(&m_UART4_Handler, (u8 *)m_dma_rx4_buff, UART4_DMA_RX_BUFF_LEN); //开始接受
#endif
__HAL_DMA_CLEAR_FLAG(&m_UART4TxDMA_Handler, DMA_FLAG_TCIF3_7);
__HAL_DMA_ENABLE_IT(&m_UART4TxDMA_Handler, DMA_IT_TC);
HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
HAL_NVIC_SetPriority(DMA1_Stream4_IRQn,0,3);
HAL_NVIC_EnableIRQ(UART4_IRQn); //使能USART1中断通道
HAL_NVIC_SetPriority(UART4_IRQn,0,1); //抢占优先级,子优先级
}
//使能去使能串口1中断
void setUart4NVIC(bool flag)
{
if (flag)
{
HAL_NVIC_EnableIRQ(UART4_IRQn);
}
else
{
HAL_NVIC_DisableIRQ(UART4_IRQn);
}
}
//使能去使能DMA接受中断
void setDMATx4NVIC(bool flag)
{
if (flag)
{
HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
}
else
{
HAL_NVIC_DisableIRQ(DMA1_Stream4_IRQn);
}
}
//使能去使能DMA发送中断
void setDMARx4NVIC(bool flag)
{
if (flag)
{
HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);
}
else
{
HAL_NVIC_DisableIRQ(DMA1_Stream2_IRQn);
}
}
//串口发送数据
void Uart4DMATx(u8 *pData, u32 len)
{
g_txFlag = 1;
#if UART4_BULK_TX
setDMATx4NVIC(false); //关闭串口DMA发送中断
Uart4_Write_Queue_Bulk(&uart4_TxQueue, pData, len);
setDMATx4NVIC(true);
u8 *pTx = NULL;
int txLen = Uart4_Read_Queue_Bulk(&uart4_TxQueue, &pTx);
if ((m_UART4_Handler.gState == HAL_UART_STATE_READY)
&& (m_UART4_Handler.hdmatx->State == HAL_DMA_STATE_READY)) //串口空闲,直接发送数据
{
HAL_UART_Transmit_DMA(&m_UART4_Handler, pTx, txLen);
}
#else
int rLen = CalcNumNotRead(&uart4_TxQueue);
if ((rLen == 0) && (m_UART4_Handler.gState == HAL_UART_STATE_READY)
&& (m_UART4_Handler.hdmatx->State == HAL_DMA_STATE_READY)) //缓存里没有要等待发送的数据,并且串口空闲,直接发送数据
{
memcpy((void *)m_dma_tx4_buff, pData, len);
HAL_UART_Transmit_DMA(&m_UART4_Handler, (uint8_t *)m_dma_tx4_buff, len);
return;
}
setDMATx4NVIC(false); //关闭串口DMA发送中断
for (int i=0;i<len;i++)
{
Uart4_Write_Queue(&uart4_TxQueue, pData);
}
setDMATx4NVIC(true);
#endif
}
//读取串口接受数据
int Uart4DMARxData(u8 *pData, int maxLen)
{
int index = 0;
while(index <= maxLen)
{
if (!Uart4_Read_Queue(&uart4_RxQueue, pData+index))
{
break;
}
index++;
}
return index;
}
bool getUart4RxFrameState()
{
return m_rx4FrameState;
}
void setUart4RxFrameState(bool state)
{
m_rx4FrameState = state;
}
//串口1中断服务程序
void UART4_IRQHandler(void)
{
static u32 len = 0;
if(__HAL_UART_GET_FLAG(&m_UART4_Handler,UART_FLAG_IDLE) != RESET) //接收空闲中断
{
__HAL_UART_CLEAR_IT(&m_UART4_Handler,UART_CLEAR_IDLEF);
#if UART4_RX_USE_DMA
m_UART4_Handler.RxState = HAL_UART_STATE_READY;
HAL_DMA_Abort(m_UART4_Handler.hdmarx);
//HAL_UART_RxDMAStop(&m_UART4_Handler);
//SCB_InvalidateDCache(); //同步内存和dcache
SCB_CleanInvalidateDCache_by_Addr((u32 *)m_dma_rx4_buff, (UART4_DMA_RX_BUFF_LEN/4));
len = UART4_DMA_RX_BUFF_LEN - m_UART4_Handler.hdmarx->Instance->NDTR;
if (len > 0)
{
for(int i=0;i<len;i++)
{
Uart4_Write_Queue(&uart4_RxQueue, m_dma_rx4_buff);
}
m_rx4FrameState = true;
}
//SCB_CleanDCache();
HAL_UART_Receive_DMA(&m_UART4_Handler, (u8 *)m_dma_rx4_buff, UART4_DMA_RX_BUFF_LEN); //开始接受
#else
if (len > 0)
{
for(int i=0;i<len;i++)
{
Uart4_Write_Queue(&uart4_RxQueue, m_dma_rx4_buff);
}
m_rx4FrameState = true;
g_rxFlag = 1;
g_rxLedNum = 10;
}
len = 0;
#endif
}
else if (__HAL_UART_GET_FLAG(&m_UART4_Handler,UART_FLAG_RXNE) != RESET)
{
#if !UART4_RX_USE_DMA
__HAL_UART_CLEAR_IT(&m_UART4_Handler,UART_FLAG_RXNE);
m_dma_rx4_buff[len++] = (u8)(m_UART4_Handler.Instance->RDR & (u8)m_UART4_Handler.Mask);
#endif
}
else if (__HAL_UART_GET_FLAG(&m_UART4_Handler,UART_FLAG_ORE) != RESET)
{
__HAL_UART_CLEAR_IT(&m_UART4_Handler,UART_FLAG_ORE);
}
else if (__HAL_UART_GET_FLAG(&m_UART4_Handler,UART_FLAG_NE) != RESET)
{
__HAL_UART_CLEAR_IT(&m_UART4_Handler,UART_FLAG_NE);
}
else if (__HAL_UART_GET_FLAG(&m_UART4_Handler,UART_FLAG_FE) != RESET)
{
__HAL_UART_CLEAR_IT(&m_UART4_Handler,UART_FLAG_FE);
}
else if (__HAL_UART_GET_FLAG(&m_UART4_Handler,UART_FLAG_PE) != RESET)
{
__HAL_UART_CLEAR_IT(&m_UART4_Handler,UART_FLAG_PE);
}
//HAL_UART_IRQHandler(&m_UART4_Handler);
}
//串口4DMA接受完成中断服务程序
void DMA1_Stream2_IRQHandler(void)
{
if (__HAL_DMA_GET_FLAG(&m_UART4RxDMA_Handler, DMA_FLAG_TCIF2_6) != RESET) //接受中断,防止溢出
{
__HAL_DMA_CLEAR_FLAG(&m_UART4RxDMA_Handler, DMA_FLAG_TCIF2_6);
for(int i=0;i<UART4_DMA_RX_BUFF_LEN;i++)
{
Uart4_Write_Queue(&uart4_RxQueue, m_dma_rx4_buff);
}
HAL_DMA_Abort(m_UART4_Handler.hdmarx);
m_UART4_Handler.RxState = HAL_UART_STATE_READY;
//HAL_UART_RxDMAStop(&m_UART4_Handler);
HAL_UART_Receive_DMA(&m_UART4_Handler, (u8 *)m_dma_rx4_buff, UART4_DMA_RX_BUFF_LEN); //开始接受
}
else if (__HAL_DMA_GET_FLAG(&m_UART4RxDMA_Handler, DMA_FLAG_TEIF2_6) != RESET)
{
__HAL_DMA_CLEAR_FLAG(&m_UART4RxDMA_Handler, DMA_FLAG_TEIF2_6);
}
else if (__HAL_DMA_GET_FLAG(&m_UART4RxDMA_Handler, DMA_FLAG_DMEIF2_6) != RESET)
{
__HAL_DMA_CLEAR_FLAG(&m_UART4RxDMA_Handler, DMA_FLAG_DMEIF2_6);
}
else if (__HAL_DMA_GET_FLAG(&m_UART4RxDMA_Handler, DMA_FLAG_FEIF2_6) != RESET)
{
__HAL_DMA_CLEAR_FLAG(&m_UART4RxDMA_Handler, DMA_FLAG_FEIF2_6);
}
//HAL_DMA_IRQHandler(&m_UART4RxDMA_Handler);
}
//串口4DMA发送完成中断服务程序
void DMA1_Stream4_IRQHandler(void)
{
u8 *pTx = NULL;
int txLen = 0;
if (__HAL_DMA_GET_FLAG(&m_UART4TxDMA_Handler, DMA_FLAG_TCIF0_4) != RESET) //发送完成中断
{
__HAL_DMA_CLEAR_FLAG(&m_UART4TxDMA_Handler, DMA_FLAG_TCIF0_4);
#if UART4_BULK_TX
m_UART4_Handler.gState = HAL_UART_STATE_READY;
HAL_DMA_Abort(m_UART4_Handler.hdmatx);
//HAL_UART_TxDMAStop(&m_UART4_Handler);
txLen = Uart4_Read_Queue_Bulk(&uart4_TxQueue, &pTx);
if (txLen > 0)
{
HAL_UART_Transmit_DMA(&m_UART4_Handler, pTx, txLen);
}
#else
int len = 0;
len = CalcNumNotRead(&uart4_TxQueue);
len = len<UART4_DMA_TX_BUFF_LEN?len:UART4_DMA_TX_BUFF_LEN;
if (len > 0)
{
for (int i=0;i<len;i++)
{
Uart4_Read_Queue(&uart4_TxQueue, (u8 *)m_dma_tx4_buff+i); //可以考虑使用二维数组来增加发送效率
}
}
m_UART4_Handler.gState = HAL_UART_STATE_READY;
HAL_DMA_Abort(m_UART4_Handler.hdmatx);
//HAL_UART_TxDMAStop(&m_UART4_Handler);
if (len > 0)
{
HAL_UART_Transmit_DMA(&m_UART4_Handler, (u8 *)m_dma_tx4_buff, len);
}
#endif
}
//HAL_DMA_IRQHandler(&m_UART4TxDMA_Handler);
}
The difficulty of the project lies in QRS filtering. The algorithm here is as follows:
/* Include Files */
#include <math.h>
#include <string.h>
#include "QRSDetection.h"
float m_minHeightSet = MIN_PEAK_HEIGHT; //设置最小peak高度
unsigned int m_peakBlankCntSet = MIN_PEAK_BLANK_CNT; //设置peak空白长度
/* Variable Definitions */
static float m_minHeight = MIN_PEAK_HEIGHT; //动态最小高度
static unsigned long long m_dataCnt = 0; //数据序号
static bool m_qrsFlag = false; //是否R点
static int m_heightCnt = 0; //R peak序号
static float maxPeak = 0.0; //peak
static float lastDatum = 0.0; //上一个值
static int timeSinceMax = 0; //最大值延时
static int count = 0; //上一个R点起计数
static int qpkcnt = 0; //起始调整高度计数
static int lastQRSDelay = 0; //RR间隔
static int preBlankCnt = 0; //peak后空白计数
static float tempPeak = 0.0; //记录上一个peak
static int findCnt = 0; //无peak计数
static int m_noiseCnt = 0; //noise计数
static float m_meanNoise = 0.0; //平均噪音
static int MS80()
{
int FS = 500;
return 80 * FS / 1000;
}
static int MS95()
{
int FS = 500;
return 95 * FS / 1000;
}
static int MS196()
{
int FS = 500;
return 196 * FS / 1000;
}
static int MS360()
{
int FS = 500;
return 360 * FS / 1000;
}
static int MS1000()
{
int FS = 500;
return 1000 * FS / 1000;
}
static int MS1650()
{
int FS = 500;
return 1650 * FS / 1000;
}
static bool isZero(float v)
{
return (fabs(v) <= 0.000001)?true:false;
}
//每8个R点动态计算R点高度阈值
static float calcMinHeight(float v, bool flag)
{
static float peak[R_PEAK_AVG_CNT] = {0};
float sum = 0.0;
peak[m_heightCnt] = v;
m_heightCnt = (m_heightCnt+1)%R_PEAK_AVG_CNT;
if (flag)
{
for(int i=0;i<R_PEAK_AVG_CNT;i++)
{
sum += peak;
}
sum = sum/R_PEAK_AVG_CNT/3; //使用平均R值的三分之一作为新的高度阈值 sqrt(1/3) = 0.57
if (sum < m_minHeightSet)
{
sum = m_minHeightSet;
}
}
return sum;
}
//计算噪音
static float calcNoise(float v, bool flag)
{
static float noise[R_PEAK_AVG_CNT] = {0};
float sum = 0.0;
noise[m_noiseCnt] = v;
m_noiseCnt = (m_noiseCnt+1)%R_PEAK_AVG_CNT;
if (flag)
{
for(int i=0;i<R_PEAK_AVG_CNT;i++)
{
sum += noise;
}
sum = 1.5*sum/R_PEAK_AVG_CNT; //sqrt(1.5) = 1.2
}
return sum;
}
/*
* Description:
* QRS Dectector based on the band-pass filtered ECG signal.
* The QRS detection rules reference the PIC based QRS detector
* implemented by Patrick S. Hamilton in picqrs.c
* from Open Source ECG Analysis (OSEA)
* http://www.eplimited.com/confirmation.htm
*
* INPUT:
* u : bandpass filtered and moving averaged ECG signal
*
* OUTPUTS:
* heartRate: detected heart rate (beat per minute)
* peak : detected local peaks (mainly R peaks)
* threshold: the detection threshold is calculated based on
* the mean estiamtes of the average QRS peak and
* the average noise peak.
*
* Detection Rules:
* Rule 1. Ignore all peaks that precede or follow larger peaks by less
* than 196 ms (306bpm). The EC13 Standard specifies that the
* QRS detector should accurately detect QRS complexes at rates
* as high as 250 beats-per-minute (bpm), and should not report
* heart rates lower than 250bpm when rates as high as 300bpm occur.
* Rule 2. If a peak occurs, check to see whether the raw signal
* contained both positive and negative slopes. If yes,
* report a peak being found, otherwise, the peak
* represents a baselien shift.
* Rule 3. If the peak is larger than the detection threshold
* classify it as a QRS complex, otherwise classify
* it as noise.
* Rule 4. If no QRS has been detected within 1.5 R-to-R intervals,
* there was a peak that was larger than half the
* detection threshold, and the peak followed the preceding
* detection by at least 360ms, classify that peak as a QRS
* complex.
* Arguments : const double u[2000]
* short *RRIntvl
* double *peak
* double *loc
* short *qrsIndex
* double *b_threshold
* Return Type : void
*/
void QRSDetection(float *u, int len, int *RRIntvl, float *peak, unsigned long long
*loc, int *delay)
{
int idx;
float peakTemp;
float peakTemp1;
*peak = 0.0;
*loc = 0;
/* Prevents detections of peaks smaller than 150 uV. */
m_dataCnt += len;
m_qrsFlag = false;
for (idx = 0; idx < len; idx++)
{
/* For a frame of input signal, process one sample at a time */
/* peak() takes a datum as input and returns a peak height */
/* when the signal returns to half its peak height, or it has been */
/* 95 ms since the peak height was detected. */
/* function QRSDetection */
peakTemp = 0.0;
u[idx] = u[idx]*fabs(u[idx]);
if (timeSinceMax > 0) {
timeSinceMax++;
}
if ((u[idx] > lastDatum) && (u[idx] > maxPeak))/* rising slope */
{
maxPeak = u[idx];
if (maxPeak > MAX_PEAK_HEIGHT)
{
/* reset timeSinceMax */
timeSinceMax = 1U;
}
}
else if (u[idx] < maxPeak/2) //find a peak
{
/* middle of falling slope */
/* Less than half the peak height */
peakTemp = maxPeak;
maxPeak = 0.0;
timeSinceMax = 0U;
}
else
{
if (timeSinceMax > MS95()) {
peakTemp = maxPeak;
maxPeak = 0.0;
timeSinceMax = 0U;
}
}
lastDatum = u[idx];
//peak must bigger than min height
if (peakTemp < m_minHeight)
{
peakTemp = 0.0;
findCnt++;
if (findCnt > MS1650()) //1650ms内没有peak,则动态高度变成最小高度
{
findCnt = 0;
m_minHeight = m_minHeightSet;//
m_meanNoise = 0.0;
qpkcnt = 0; //重新等待8个点计算最小高度
}
}
else
{
findCnt = 0;
}
/* returns adjusted peak */
/* there can only be one QRS complex in any 196ms (306bpm) window */
if (isZero(peakTemp) && (preBlankCnt == 0))
{
peakTemp1 = 0.0;
}
else if (isZero(peakTemp) && (preBlankCnt != 0))
{
/* if we have held onto a peak for 196ms, pass it on for eval */
preBlankCnt--;
if (preBlankCnt == 0) {
peakTemp1 = tempPeak; //R
} else {
peakTemp1 = 0.0;
}
}
else if (!isZero(peakTemp) && (preBlankCnt == 0))
{
/* if there has been no peak for 196ms, save this one and start */
/* counting */
tempPeak = peakTemp;
preBlankCnt = m_peakBlankCntSet;//MS196();
peakTemp1 = 0.0;
}
else
{
/* (pk~=0) && (preBlankCnt~=0) */
/* if we were holding a peak, but this one is bigger, */
/* save it and start counting to 196ms again */
if (peakTemp > tempPeak) { //peakTemp is noise
if (qpkcnt >= R_PEAK_AVG_CNT)
{
m_meanNoise = calcNoise(tempPeak, true);
}
else
{
m_meanNoise = calcNoise(tempPeak, false);
}
tempPeak = peakTemp;
preBlankCnt = m_peakBlankCntSet;//MS196();
peakTemp1 = 0.0;
} else {
preBlankCnt--;
if (preBlankCnt == 0) {
peakTemp1 = tempPeak; //R
} else {
peakTemp1 = 0.0;
}
}
}
count++;
/* Initialize the QRS peak buffer with the first eight */
/* local maximum peaks detected */
//if ((peakTemp1 >= m_minHeight)&& (peakTemp1 >= m_meanNoise)) //R点,先取消噪音检测
if (peakTemp1 >= m_minHeight)
{
if (qpkcnt < R_PEAK_AVG_CNT) {
if (!isZero(peakTemp1)) {
//m_qrsFlag = true;
qpkcnt++;
if (qpkcnt == R_PEAK_AVG_CNT) {
m_minHeight = calcMinHeight(peakTemp1, true);
count = 0;
}
else
{
m_minHeight = calcMinHeight(peakTemp1, false);
}
}
} else {
/* qpkcnt >= 8 */
m_minHeight = calcMinHeight(peakTemp1, true);
m_qrsFlag = true;
lastQRSDelay = count;
count = 0;
}
}
//R
if (m_qrsFlag) {
*peak = peakTemp1;
*loc = m_dataCnt-(len)+idx-m_peakBlankCntSet; //MS196()
*RRIntvl = lastQRSDelay;
*delay = m_dataCnt-(*loc+1);
m_qrsFlag = false;
}
}
}
/*
* Arguments : void
* Return Type : void
*/
void maxPeak_not_empty_init(void)
{
maxPeak = 0.0;
lastDatum = LOWEST_PEAK_VALUE;
timeSinceMax = 0U;
}
/*
* Arguments : void
* Return Type : void
*/
void qrs_det_init(void)
{
count = 0;
qpkcnt = 0;
lastQRSDelay = 0;
/* EC13: QRS detector should not detect QRS complexes with */
/* amplitudes of less than 0.15mV and 1mV QRS complexes with width */
/* less than 10ms (6000bpm) */
m_minHeight = m_minHeightSet;
tempPeak = 0.0;
preBlankCnt = 0;
m_heightCnt = 0;
m_meanNoise = 0;
m_noiseCnt = 0;
}
void QRSInit()
{
maxPeak_not_empty_init();
qrs_det_init();
m_dataCnt = 0;
}
float getDynamicMinHeight()
{
return m_minHeight;
}
float getDynamicNoise()
{
return m_meanNoise;
}
void setMinHeight(float value)
{
m_minHeightSet = value;
}
void setPeakBlankCnt(unsigned int cnt)
{
m_peakBlankCntSet = cnt;
}
5. Demonstration video of the work’s functions
f9c07f83e86c8a63cdcf48a8660b4126
6. Project Summary
In fact, the difficulty lies in ADAS data filtering, how to obtain a clean waveform.
|