/********************************************************************* Author : Tan Xiaojun www.analog.com/MicroConverter Date : Dec,2,2005 File : ECG3.0-060323.c Hardware : ADuC7020 Description : Reference : ADCtimer.c(C:\ADuC_Beta702x\code\Keil Code Examples \ADuC7020\ADC\ADCtimer\ADCtimer.c) *********************************************************************/ #include // Include ADuC7020 Header File #define sample 177 //WS: ADuC7020 设置为采样率200Hz, 但PC端接收到的数据为177/8 Hz. 可能是串口速度太慢,而且是在加心率算法后的结果 #define baseline 2000 // WS: 本想设计一个算法跟踪基线漂移,但现在没有实现。故此变量没有用到 int adc,hr=0; int ecgadc0=0,ecgadc1=0,ecgadc2=0,ecgadc3=0,ecgadc4=0; //This part of variable is sample and rhythm of heart. 滑动平均时用,但是实际并没有用到 int sequ=0,Rtime=100,Stime=0,Rwaveold=0,Rwavenew=0; //This part of variable is concerned with R wave location int Rwave[2],r=0; //and rhythm calculate. WS: 计算一次心率需要2各R波 int RRinterval[4]={0,0,0,0},*rr,RRaverage=0,j=0; //ws: 计算R-R间的平均值 int Max=0,Min=5000,Maxold=0,Maxnew=0; //This part of variable is concerned with threshold setting. 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; //Some flag variable. 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((++sequpMin))Min=pMin; Maxthreshold=Min+((Max-Min)>>1); Minthreshold=Min+(((Max-Min)*3)>>3); return; } if(sequ>3000)Parameter_reset(); //If have not find QRS complex, reset the parameter. 遇到心电信号突然改变时,较长时间找不到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; }