FastBond体外诊断——便携式12导联心电仪
一个超级小巧的便携式12导联心电仪,使用到了美信以及ADI的芯片进行设计。
标签
嵌入式系统
冷月烟
更新2021-11-29
1722

1.项目介绍

该心电仪设计的时候主要考虑了以下几点:

1.体积足够小,足够便携。让用户对他的体积重量的感知尽可能的低。

2.设备足够安全。所有与医疗有关的设备,无论是医疗器械,还是辅助诊断设备,都要保证不对使用者的安全有不好的影响。更不能有危险的存在。

3.设备代码有保护,不会轻易被别人所复制。不会产生设计了一个产品,然后被人轻易读出代码,然后抄板复制。

基于以上几点,我设计了这一款12导联的心电仪。

硬件部分,采用STM32F103作为主控,ADS1298作为心电波形采样主芯片。美信的DS2411R作为软件加密芯片。ADI的ADP151作为供电的LDO。使用3节干电池作为供电,这样安全性比较高。使用蓝牙进行通信,支持TF卡功能。

软件部分:可使用蓝牙实时上传采集到的心电数据,并可以使用对应的接收软件解析出来。也可将心电数据保存到TF卡里面,然后把文件读出来解析出相关数据。板子预留一个按键可以作为扩展功能使用,比如紧急呼叫,一键暂停或者开始,通知设备上传数据到云端。

上位机部分:使用C#编写,可以接收设备上传的信号,能够显示12条导联波形,显示设备当前电量,得到按键状态等功能。用于辅助测试以及演示使用。该项目原本计划还有一个手机端的软件,受限于时间并没有完成。

最开始计划使用ADAS1000加MAX32660然后使用LTC6655作为模拟部分基准芯片,放弃的原因也很简单,成本太高了,远远超出预算。最后选择了当前展示的方案。

2.项目用到的仪器器件

仪器

1.心电模拟器:可产生1Hz的方波信号,60BPM的正常心电波形,100BPM心电波形。

FnIleyVYL98ry3yVlExvlKD01f2u

2.示波器:用于采集模拟端输出的信号,以及判断通信波形是否正常。

3.万用表:用于测量板子电源是否正常工作,以及相关功耗性能测试。

器件

1.ADI的ADP151

超低功耗,超低压差的LDO芯片,用于给ADS1298提供3.3V的模拟电源。性能非常优异。200 mA负载时工作电源电流低至265 μA,因此ADP151适合电池供电的便携式设备。

FvG7cIaYtdVP2iEHGUa3GHcGlZ7t

2.美信的DS2411R

非常经典的加密芯片,通过单总线接口可以巷设备提供一个唯一ID用于加密功能,当然,比起目前可以将部分代码放到加密芯片里面的新型加密芯片来说,DS2411R是有一点落后,不过SOT23-3的封装,加上目前出开盖外没啥有效的破解手段的原因。使用这个芯片还是完全能够应对此项目的需求的。

FqGvv6wT5CBgmIUohuYCifO0ag3i

3.ADS1298:8通道、24位模拟前端。与普通的模/数转换芯片相比,ADS1298更多的优势在于其便携性、紧凑性、低功耗性。该芯片的集成特性包括8路独立的PGA和24b ADC,右腿驱动电路以及电极检测等。

3.关键代码与说明

1.ADS1298驱动代码

ADS1298_SPI_Init();

ADS1298_CS_L;
delay_us(1);
SPIx_ReadWriteByte(ADS1298_SDATAC);

while(device_id != 0x92)       //识别芯片型号,1298为0x92
{
    device_id = ADS1298_SPIReadReg(ADS1298_ID);
//		printf("ID:0X%02X\n",device_id);
    delay_ms(100);
}

ADS1298_SPIWriteReg(ADS1298_CONFIG1, 0X46); //LP 250    0XC6); //HR 500 //
ADS1298_SPIWriteReg(ADS1298_CONFIG2, 0X34);
ADS1298_SPIWriteReg(ADS1298_CONFIG3, 0XCC);
ADS1298_SPIWriteReg(ADS1298_LOFF, 0X33);

ADS1298_SPIWriteReg(ADS1298_CH1SET, 0X60); //12 正常
ADS1298_SPIWriteReg(ADS1298_CH2SET, 0X60); 
ADS1298_SPIWriteReg(ADS1298_CH3SET, 0X60); 
ADS1298_SPIWriteReg(ADS1298_CH4SET, 0X60); 
ADS1298_SPIWriteReg(ADS1298_CH5SET, 0X60); 
ADS1298_SPIWriteReg(ADS1298_CH6SET, 0X60); 
ADS1298_SPIWriteReg(ADS1298_CH7SET, 0X60); 
ADS1298_SPIWriteReg(ADS1298_CH8SET, 0X60); 

ADS1298_SPIWriteReg(ADS1298_RLD_SENSP, 0X00);
ADS1298_SPIWriteReg(ADS1298_RLD_SENSN, 0X00);
ADS1298_SPIWriteReg(ADS1298_LOFF_SENSP, 0X00);
ADS1298_SPIWriteReg(ADS1298_LOFF_SENSN, 0X00);

ADS1298_SPIWriteReg(ADS1298_PACE, 0X01);
ADS1298_SPIWriteReg(ADS1298_CONFIG4, 0X02);
ADS1298_SPIWriteReg(ADS1298_WCT1, 0X0A);
ADS1298_SPIWriteReg(ADS1298_WCT2, 0XE3);

SPIx_ReadWriteByte(ADS1298_RDATAC);
ADS1298_START_H;       //转换开始

2.DS2411R驱动代码

//初始化DS2411的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在    	 
uint8_t DS2411_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能PA端口时钟

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;				 //LED0-->PA.8 端口配置
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA,GPIO_Pin_2);						 //PA.8 输出高

    DS2411_Rst();
    return DS2411_Check();
}  
//从DS2411得到SN
short DS2411_Get_SN(void)
{
    uint8_t SN[8];
    DS2411_Rst();
    if(DS2411_Init())printf("DS2411 Check Failed!");	 
    DS2411_Write_Byte(0x33);// Read ROM	    
    SN[0]=DS2411_Read_Byte(); //0 
    SN[1]=DS2411_Read_Byte(); //1
    SN[2]=DS2411_Read_Byte(); //2
    SN[3]=DS2411_Read_Byte(); //3
    SN[4]=DS2411_Read_Byte(); //4
    SN[5]=DS2411_Read_Byte(); //5
    SN[6]=DS2411_Read_Byte(); //6
    SN[7]=DS2411_Read_Byte(); //7
    printf("%.2X-%.2X-%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",SN[0],SN[1],SN[2],SN[3],SN[4],SN[5],SN[6],SN[7]);	
    return 0;    
}

3.代码主逻辑

void TIM3_IRQHandler(void)   //TIM3中断
{
	static uint8_t flag = 0;
	static uint8_t s = 0;
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
	{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);  //清除TIMx的中断待处理位:TIM 中断源 
		if(SWITCH == 1)
		{
			if(flag == 0)
				mode = 0;
			else
				flag = 0;
		}
		else
		{
			if(flag == 1)
				mode = 1;
			else
				flag = 1;
		}
		
		if(mode == 0)
		{
			if(ble_ecg.state != 0)
			{
				LED0 = 1;
				LED1 = 0;
			}
			else
			{
				LED0 = 1;
				LED1 = s%2;
			}
		}
		else
		{
			if(sd_ecg.state != 0)
			{
				LED0 = 0;
				LED1 = 1;
			}
			else
			{
				LED0 = s%2;
				LED1 = 1;
			}
		}
		s++;
		
	}
}

 
void EXTI4_IRQHandler(void)
{
	uint16_t i;
	uint16_t temp = 0;
  	if(EXTI_GetITStatus(EXTI_Line4) != RESET)	  //检查指定的EXTI0线路触发请求发生与否
	{	  
		ADS1298_Read(buff);
		if(mode == 0)
		{
			if(ble_ecg.state != 0)
			{
				if(DMA_GetFlagStatus(DMA1_FLAG_TC4) == SET)//等待通道4传输完成
				{
					DMA_ClearFlag(DMA1_FLAG_TC4);  //清除发送完成标志
					
					ble_ecg.adc_state = 0;
					ble_ecg.key_state = 0;
					
					SendBuff[0] = 0xAA;
					SendBuff[1] = 0xAA;			
					SendBuff[2] = 0X08;
					
					for(i = 3; i < 27; i++)
					{
						SendBuff[i] = buff[i];
					}
					
					SendBuff[27] = ble_ecg.key + (ble_ecg.adc_v & 0xFE);
					for(i = 3; i <= 27; i++)
					{
						temp += SendBuff[i];
					}							
					
					SendBuff[28] = temp /0XFF + temp % 0XFF;
					MYDMA_Enable(DMA1_Channel4);//开始一次DMA传输!
				}
			}
		}
		else
		{
			if(sd_ecg.state != 0)
			{
				if(sd_ecg.ads_data->num < 260)
				{
					for(i=0; i<8; i++)
					{
						if(buff[3+i*3] & 0x80)  sd_ecg.ads_data->data[sd_ecg.ads_data->num][i] = ((uint32_t)0x000000FF<<24) | ((uint32_t)buff[3+i*3]<<16) | ((uint32_t)buff[3+i*3+1]<<8) | ((uint32_t)buff[3+i*3+2]);
						else                    sd_ecg.ads_data->data[sd_ecg.ads_data->num][i] = ((uint32_t)buff[3+i*3]<<16) | ((uint32_t)buff[3+i*3+1]<<8) | ((uint32_t)buff[3+i*3+2]);
					}
					sd_ecg.ads_data->num++;
					sd_ecg.state = 2;	
//					if(eeer!=0)
//					{
//						err_jishu ++;
//						printf("%d\r\n",eeer);
//						eeer = 0;
//					}					
				}
//				else
//				{
//					eeer++;
//				}
			}
		}
	}
	EXTI_ClearITPendingBit(EXTI_Line4);  //清除EXTI0线路挂起位
}

4.功能演示

1.接心电模拟器

Fgw70UgtJqg2Y8lfTB3bPRy0P2Pw

Fh0bXNWJdjHJgmR9t7lB0GDnUrxi

1Hz的方波信号

Flgxt4Bm28_G0auxhUbwXJ7Ja1q0

60BPM的正常心电波形

FgGd9CYGORQIJLMCyxn6FI83Rya3

100BPM心电波形

2.测量人体心电

未在身上涂酒精,而且为了方便录制视频是坐着测量的。如果涂酒精且平躺静止,效果要好很多。

FrWXBg2c1MbrgwFx4AGjk3hiS8X6

5.项目遇到的问题与解决

1.测量不到波形不知道哪里问题。

查阅芯片寄存器手册得知有测试模式,将其调整为测试模式后,由内部产生1Hz方波,得知是接口配置问题,修正后解决。

2.测量到的波形在一些条件下会出现非常严重的跳变

ADS1298的数据为24位补码,需针对这个情况对读出的原始数据进行转化。

3.基线漂移非常严重

在上位机代码里面追加滤波算法,追加动态y轴范围。显示效果好了很多。

4.对12导联输出不太理解怎么回事

查阅相关资料才知道,aVR、aVL、aVF是计算出来的。

6.心得体会

心得:参加这次FastBond的时间还是比较晚的,当时在选题的时候就很纠结,因为可选的题目还是很多的,最终决定挑战一下自己,做了这个心电仪,最终的结果还是比较满意的吧,也很高兴能参加这一次的活动。学习到了很多东西。

建议:官方可以更多的推荐一些芯片以及方案,最好是那些比较新的方案,这样选型的时候思维就更广阔。

软硬件
电路图
附件下载
程序.zip
程序
团队介绍
团队成员
冷月烟
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号