📄 ecg.c
字号:
//Hardware : ADuC7020
//Reference : ADCtimer.c(C:\ADuC_Beta702x\code\Keil Code Examples
// \ADuC7020\ADC\ADCtimer\ADCtimer.c)
#include <ADuC7020.h> // Include ADuC7020 Header File
#define sample 177
// ADuC7020设置为采样率200Hz, 但PC端接收到的数据为177/8 Hz.
//可能是串口速度太慢,而且是在加心率算法后的结果
#define baseline 2000
int adc,hr=0;
int ecgadc0=0,ecgadc1=0,ecgadc2=0,ecgadc3=0,ecgadc4=0;
//这部分变量是采样和心率,滑动平均时用,但是实际并没有用到
int sequ=0,Rtime=100,Stime=0,Rwaveold=0,Rwavenew=0;
//这部分变量处理R波位置和心率计算, 计算一次心率需要2个R波
int Rwave[2],r=0;
int RRinterval[4]={0,0,0,0},*rr,RRaverage=0,j=0;
// 计算R-R间的平均值
int Max=0,Min=5000,Maxold=0,Maxnew=0;
//这部分变量是关于阀值设置的.
int Maxthreshold,Minthreshold;
int pMax,pMin;
int Max_Minarray[6]={2000,2000,2000,2000,2000,2000},temp=0;
char ignoflag=1,threflag=1,calflag=0,reversalflag=0;
char HRcountflag=0,aflag=0,iflag=0,maxflag=0;
/*int count=0,countp=0,countpold=0,countpnew=0;
//these variables are used to count QRS complex and to follow baseline.
long base=0;
//But baseline following has not finished yet.
int basec=0,baseave=2000;
char normalflag=1;
int QRSwave=0;*/
void initialization_chip(); //Initialize ADuC7020.
void ADCpoweron(int); //ADC need power on time.
void Leadchoose(int);
void Modechoose(int);
void failure(void); //If communicate with PC failed
void ADC_interrupt();
void UART_interrupt();
void Parameter_reset();
int findMax();
int findMin();
int main (void)
{
short int lead,mode,leadflag=1,modeflag=1;
char trx;
initialization_chip();
//from initialization_chip_060323.c to suit for hardware version 3.0
while(leadflag)
{
leadflag=0;
while(!(0x01==(COMSTA0 & 0x01))){}
//wait PC's data
trx=COMRX;
lead=(short int)trx;
switch(lead)
{
// Lead input ADG658+ ADG658-
case '1': Leadchoose(0x3ff90000); break;
//LeadI , LA and RA, S8 S5
case '2':Leadchoose(0x3fe10000); break;
//LeadII , LL and RA, S2 S5
case '3': Leadchoose(0x3fe30000); break;
//leadIII, LL and LA, S2 S7
case '4': Leadchoose(0x3ff50000); break;
//1mv cal, LL and AGND S4 S6
case '5': Leadchoose(0x3fec0000);break;
//aVR , RA and L+F, S6 S2
case '6': Leadchoose(0x3ffa0000);break;
//aVL , La and R+F, S8 S3
case '7': Leadchoose(0x3fe60000);break;
//aVF , LL and R+L, S2 S4
case '8':Leadchoose(0x3fc000ff);break;
//V1 , C and R+L+F, S1 S1 the difference with case
case '9':Leadchoose(0x3fc00000);break;
//V2~V6 , C and R+L+F, S1 S1 8 and 9,because Vi need
default : failure();leadflag=1; // reversal
}
}
while(modeflag)
{
modeflag=0;
while(!(0x01==(COMSTA0 & 0x01))){}
trx=COMRX;
mode=(short int)trx;
switch(mode)
{
// Mode ADG704
case '1': Modechoose(0x0000c000);break; //monitor S4
case '2': Modechoose(0x00000000);break; //diagnostic S1
case '3': Modechoose(0x00008000);break; //surgery S3
default: failure();modeflag=1;
}
}
IRQEN = ADC_BIT+UART_BIT; // Enable ADC and UART IRQ ( 0x80 )
while(1){ } //waiting interrupt
return 0 ;
}
/********************************************************************/
/* IRQ Service Program */
/********************************************************************/
void IRQ_Handler(void)__irq
//Do NOT change the writing format about interrupt program. 不要改函数名
{
if(IRQSIG&0x00004000)
{
UART_interrupt(); //Do NOT change the interrupt program name.
}
if(IRQSIG&0x00000080)
{
ADC_interrupt();
}
}
void ADC_interrupt()
{
int s=0;
char send,da;
int ecgsum,ecgwave;
GP0DAT ^= 0x00400000;
// Complement P0.6 WS: V3.0硬件. LED每进一次中断会改变一次状态
adc=ADCDAT>>16;
adc &= 0x00000fff;
while(!(0x020==(COMSTA0 & 0x020))){}
// output test byte 通信协议的头
COMTX = 0xA5;
send =adc>>8;
da = (char)send;
while(!(0x020==(COMSTA0 & 0x020))){}
COMTX = da; // output ECG wave high byte
send=adc&0x00ff;
da = (char)send;
while(!(0x020==(COMSTA0 & 0x020))){}
COMTX = da; // output ECG wave low byte
send=hr;
da = (char)send;
while(!(0x020==(COMSTA0 & 0x020))){}
COMTX = da; // output the rhythm of heart
while(!(0x020==(COMSTA0 & 0x020))){}
COMTX = 0xff; // output end byte
if(calflag==0xFF)
//generate the square wave for 1mV calibration,P4.2
{
if((++sequ)%100==0)GP4DAT^=0x00040000;
if(sequ==3000)sequ=0;
return;
}
if(reversalflag)adc=4096-adc;
//If lead choosed V1 and aVR, the adc need to reversal first. 显示为实际波形。但计算心率时需要将这些信号翻转以统一算法
/*ecgadc0=ecgadc1;
//滑动平均并没有使用。原因可能是信号源及硬件滤波效果较好
ecgadc1=ecgadc2;
ecgadc2=ecgadc3;
ecgadc3=ecgadc4;
ecgadc4=adc;
ecgsum=ecgadc0;
ecgsum+=ecgadc1;
ecgsum+=ecgadc2;
ecgsum+=ecgadc3;
ecgsum+=ecgadc4;
ecgwave=ecgsum/5;*/
//++count;
if((++sequ<sample*23)&&ignoflag)return;
//the first 23secs(4600samples) are ignored,because the hardware. 由于硬件需要一定时间来稳定,故刚上电及导联切换后需要等待一段时间
ignoflag=0;
Max_Minarray[temp]=Max_Minarray[temp+3]=adc;
if(++temp==3)temp=0;
if(sequ==sample*25)
//另外附加2秒时间用来决定初始的各个阈值
{
threflag=0;
sequ=0;
}
if(threflag) //初始各个阈值决定,与后续找R波的方法不一样
{
findMax();
if((aflag)&&(Max<pMax))Max=pMax;
findMin();
if((iflag)&&(Min>pMin))Min=pMin;
Maxthreshold=Min+((Max-Min)>>1);
Minthreshold=Min+(((Max-Min)*3)>>3);
return;
}
if(sequ>3000)Parameter_reset();
//遇到心电信号突然改变时,较长时间找不到QRS波,则重置各阈值
if(HRcountflag)
{
rr=RRinterval;
RRinterval[j]=(Rwave[1]-Rwave[0]);
//made the previously MaxTime subtract from the following MaxTime
if(++j==4)j=0;
//the RRinterval is the point number between two R waves
for(s=0;s<4;s++)
{
RRaverage+=*rr; //get a average of RRinterval
rr++;
}
RRaverage>>=2; //hr=(sample rate*60)/RRaverage
hr=sample*60/RRaverage;
RRaverage=0;
HRcountflag=0;
Rtime=100;
Stime=0;
}
findMax();
if((aflag)&&(pMax>Maxthreshold))
//When adc bigger than max threshold, find the biggest one. 初步确定R波
{ //The biggest wave can be seen as the R wave.
Max=pMax;
Rtime=sequ-1;
maxflag=1;
//countp=count;
return;
}
if(maxflag) //After find the "R wave", to look for the "S wave" immediately
{
findMin();
if((iflag)&&(pMin<=Minthreshold))
{
Min=pMin;
Stime=sequ-1;
}
else return;
if(((Stime-Rtime)>0)&&((Stime-Rtime)<=12))
//Afer find the "S wave" and "R wave",judge their location interval.
{
//The interval must be very close. If the interval doesn't match,
//++QRSwave;
//that means this "R wave" is not the real R wave.
Rwaveold=Rwavenew;
Rwavenew=Rtime;
//countpold=countpnew;
//countpnew=countp;
Maxold=Maxnew;
Maxnew=pMax;
if((Rwavenew-Rwaveold)<(sample>>2))
//Then judge if this "R wave" is near with privious "R wave". 按照最高心率300次计算,在200Hz采样率下2各R波之间间隔最小为50
{
//QRSwave-=1;
if(Maxold>Maxnew)
//If both the "R wave" are closely, the bigger wave is seen as the
{ //real R wave.
Rwavenew=Rwaveold;
//countpnew=countpold;
}
}
else //Otherwise, prepare for looking for next R wave and calculate
{ //threshold by this R wave.
if(++r==2) //If find two R waves, it can set flag to count rhythm.
{
r=0;
sequ=0;
Rwavenew=0;
HRcountflag=1;
}
Maxthreshold=Min+((Max-Min)>>1);
Minthreshold=Min+(((Max-Min)*7)>>4);
}
Rwave[r]=Rwavenew; //Note this R wave.
maxflag=0;
}
}
return;
}
void Parameter_reset()
{
sequ=0;
threflag=1;
HRcountflag=0;
Max=0;
Min=5500;
Rtime=100;
Stime=0;
r=0;
RRinterval[0]=0;RRinterval[1]=0;RRinterval[2]=0;RRinterval[3]=0;
j=0;
RRaverage=0;
return;
}
void UART_interrupt()
{
char leadflag=1,modeflag=1,lead,mode,head;
while(!(0x020==(COMSTA0 & 0x020))){}
head=COMRX;
if(head==0xFF) //选导联
{
while(leadflag)
{
leadflag=0;
while(!(0x020==(COMSTA0 & 0x020))){} //wait PC's data
lead=COMRX;
switch(lead)
{
// Lead input ADG658+ ADG658-
case '1': Leadchoose(0x3ff90000); break;
//LeadI , LA and RA, S8 S5
case '2':Leadchoose(0x3fe10000); break;
//LeadII , LL and RA, S2 S5
case '3': Leadchoose(0x3fe30000); break;
//leadIII, LL and LA, S2 S7
case '4': Leadchoose(0x3ff50000); break;
//1mv cal, LL and AGND S4 S6
case '5': Leadchoose(0x3fec0000);break;
//aVR , RA and L+F, S6 S2
case '6': Leadchoose(0x3ffa0000);break;
//aVL , La and R+F, S8 S3
case '7': Leadchoose(0x3fe60000);break;
//aVF , LL and R+L, S2 S4
case '8':Leadchoose(0x3fc000ff);break;
//V1 , C and R+L+F, S1 S1
case '9':Leadchoose(0x3fc00000);break;
//V2~V6 , C and R+L+F, S1 S1
default : failure();leadflag=1;
}
}
}
if(head==0xFE) //选模式
{
while(modeflag)
{
modeflag=0;
while(!(0x020==(COMSTA0 & 0x020))){}
mode=COMRX;
switch(mode)
{ //Mode ADG704
case '1': Modechoose(0x0000c000);break; //monitor S4
case '2': Modechoose(0x00000000);break; //diagnostic S1
case '3': Modechoose(0x00008000);break; //surgery S3
default: failure();modeflag=1;
}
}
}
Parameter_reset();
return;
}
void Leadchoose(int contr)
{
int temp;
temp=GP1DAT;
temp>>2;
temp&=0x0000ffff;
temp|=contr;
temp<<2;
GP1DAT=temp;
while(!(0x020==(COMSTA0 & 0x020))){}
COMTX = 0xAA;
if((contr==0x3fc000ff)||(contr==0x3fec0000))reversalflag=1;
if(contr==0x3ff50000)calflag=0xff;
}
void Modechoose(int ctr)
{
int tem;
tem=GP1DAT;
tem>>2;
tem&=0xffff0000;
tem|=ctr;
tem<<2;
GP1DAT=tem;
while(!(0x020==(COMSTA0 & 0x020))){}
COMTX = 0xAC;
}
void failure(void)
{
while(!(0x020==(COMSTA0 & 0x020))){}
COMTX = 0xFA;
}
int findMax()
{
if((Max_Minarray[temp]<=Max_Minarray[temp+1])&&
(Max_Minarray[temp+1]>=Max_Minarray[temp+2]))
{
pMax=Max_Minarray[temp+1];
aflag=1;
return;
}
aflag=0;
return;
}
int findMin()
{
if((Max_Minarray[temp]>=Max_Minarray[temp+1])&&
(Max_Minarray[temp+1]<=Max_Minarray[temp+2]))
{
pMin=Max_Minarray[temp+1];
iflag=1;
return;
}
iflag=0;
return;
}
void initialization_chip() //this function used to suit for prototype board
{
//set core clock
POWKEY1=0x01;
POWCON=0x00; //core clock=40.96MHz,CD bit=0
POWKEY2=0xF4;
//set ADC registers
ADCpoweron(20000); // power on ADC
ADCCP = 0x02; // select ADC positive Channel 0 ;2 for temporary
ADCCN = 0x02; // select ADC negative Channel 0
ADCCON = 0x04A2; // ADC clock speed :fADC/2
// ADC acquisition time: 2clocks
// disable ADCbusy pin
// single end mode
// enable time0 as a conversion input
REFCON = 0x01; // use the internal reference for ADC conversions
// connect internal 2.5V reference to VREF pin
//Setup rx & tx pins on P1.0 and P1.1
GP1CON = 0x00000011;
//Start setting up UART at 9600bps
COMCON0 = 0x80; // Setting DLAB
COMDIV0 = 0x85;
COMDIV1 = 0x00;
COMCON0 = 0x07; // Clearing DLAB
COMIEN0=0x01; //enable receive buffer full interrupt
//timer0 configuration
T0LD = 0x3200; //12800*16/40960000Hz=5ms,0x6400for 100Hz,
T0CON = 0x000000C4; // enable timer0
// periodic mode
// prescale /16
//Set P4.2
GP4CON&= 0xfff0ffff;
GP4DAT = 0x04000000; // Configure P4.2 output low level
//Configure Multiplexer control pin
GP0CON&=0x1000ffff; //P0.4,P0.5,P0.6 as GPIO
GP0DAT=0x70000000; //P0.4,P0.5,P0.6 output low levels
GP1CON&=0x0000ffff; //P1.4,P1.5,P1.6,P1.7 as GPIO
GP1DAT=0xF0200000; //P1.4,P1.5,P1.6,P1.7 output low levels
}
/*void initialization_chip() //this is the new initialization function for hardware 3.0 控制管脚、RS-232口的定义不一样
{
//set core clock
POWKEY1=0x01;
POWCON=0x00; //core clock=40.96MHz,CD bit=0
POWKEY2=0xF4;
//Setup rx & tx pins on P0.7 and P2.0 (Mode 2)
GP0CON=0x20001000; //P0.7 SIN, P0.6 GPIO, P0.3 TRST, P0.0 BM
GP2CON=0x00000002; //p2.0 SOUT
//Set P0.6
GP0DAT=0x40000000; //light the LED
//Start setting up UART at 9600bps
COMCON0 = 0x80; // Setting DLAB
COMDIV0 = 0x85;
COMDIV1 = 0x00;
COMCON0 = 0x07; // Clearing DLAB
COMIEN0=0x01; //enable receive buffer full interrupt
//timer0 configuration
T0LD = 0x3200; //12800*16/40960000Hz=5ms,0x6400for 100Hz,
T0CON = 0x000000C4; // enable timer0
// periodic mode
// prescale /16
//set ADC registers
ADCpoweron(20000); // power on ADC
ADCCP = 0x04; // select ADC positive Channel 4 ;
ADCCN = 0x04; // select ADC negative Channel 0
ADCCON = 0x04A2; // ADC clock speed :fADC/2
// ADC acquisition time: 2clocks
// disable ADCbusy pin
// single end mode
// enable time0 as a conversion input
REFCON = 0x01; // use the internal reference for ADC conversions
// connect internal 2.5V reference to VREF pin
//Configure Multiplexer control pin
GP1CON=0x00000000;
//P1.7~P1.0 as GPIO, P1.0&P1.1 control ADG704 to select working mode(default is
//HPF 0.05Hz, LPF 112Hz).P1.2~P1.7 control two ADG658 to select lead(default is
//Lead C).P1.2~P1.4 control negative input, P1.5~P1.7 control positive input.
GP1DAT=0xff000000; //P1.7~P1.0 output low levels
GP4CON=0x00000000; //P4.2 as GPIO,control ADG719 to generate a 1mV square wave.
GP4DAT=0x04000000; //P4.2 output low levels
GP0DAT=0x40400000; //quench the LED
} */
void ADCpoweron(int time)
{
ADCCON=0x0422;
while(time>=0)--time;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -