Funpack第六期-基于MAX32660-EVSYS具有心率和血氧检测的智能手表
使用MAX32660-EVSYS、1.14寸LCD屏幕和MAX30100心率血氧检测传感器实现了日期星期和时间显示以及心率和血氧浓度的检测功能。
标签
Funpack第六期
MAX32660-EVSYS
azjhong
更新2021-03-28
1876

1.功能实现

本代码主要实现了日期星期和时间显示以及心率和血氧浓度的检测功能。

2.硬件连接

MAX32660-EVSYS

使用了MAX32600的一个SPI接口、一个I2C接口和一个UART接口。

1.14寸LCD屏引脚 板卡引脚
SCL P0_6
SDA P0_5
DC P0_12
CS P0_7
RES RSTN
VCC/GND 单独供电3.3V(防止屏幕供电对控制器USB供电影响)
MAX30100引脚  
SCL P0_8
SDA P0_9
VCC/GND USB供电
时间校准  
USB转TTL的TX P0_11
USB转TTL的RX P0_10

3.数据处理

时间显示代码

static void display_real_time(void)
{
    uint32_t ge, shi, bai, qian, tmp;
    uint8_t enum_tmp;

    r_time.sec = RTC_GetSecond();
    r_time.update.bit.usec = UPDATE_ENABLE;

    /* sec */
    tmp = r_time.sec + p_time.sec[1] * 10 + p_time.sec[0];
    if((tmp % 60) == 0)
    {
        r_time.min = tmp / 60;
        r_time.update.bit.umin = UPDATE_ENABLE;
    }
    tmp %= 60;
    ge = tmp % 10;
    shi = tmp / 10;
    if(r_time.update.bit.usec)
    {
        r_time.update.bit.usec = UPDATE_DISABLE;
        LCD_ShowIntNum(WATCH_W + 120, WATCH_H, shi, 1, WHITE, BLACK, 32);
        LCD_ShowIntNum(WATCH_W + 140, WATCH_H, ge, 1, WHITE, BLACK, 32);
    }
    /* min */
    tmp = r_time.min + p_time.min[1] * 10 + p_time.min[0];
    if((tmp % 60) == 0)
    {
        r_time.hr = tmp / 60;
        r_time.update.bit.uhr = UPDATE_ENABLE;
    }
    tmp %= 60;
    ge = tmp % 10;
    shi = tmp / 10;
    if(r_time.update.bit.umin)
    {
        r_time.update.bit.umin = UPDATE_DISABLE;
        LCD_ShowIntNum(WATCH_W + 60, WATCH_H, shi, 1, WHITE, BLACK, 32);
        LCD_ShowIntNum(WATCH_W + 80, WATCH_H, ge, 1, WHITE, BLACK, 32);
        LCD_ShowChar(WATCH_W + 100, WATCH_H - 2, ':', WHITE, BLACK, 32, 0);	
    }
    /* hr */
    tmp = r_time.hr + p_time.hr[1] * 10 + p_time.hr[0];
    if((tmp % 24) == 0)
    {
        r_time.day = tmp / 24;
        r_time.update.bit.uday = UPDATE_ENABLE;
    }
    tmp %= 24;
    ge = tmp % 10;
    shi = tmp / 10;
    if(r_time.update.bit.uhr)
    {
        r_time.update.bit.uhr = UPDATE_DISABLE;
        LCD_ShowIntNum(WATCH_W, WATCH_H, shi, 1, WHITE, BLACK, 32);
        LCD_ShowIntNum(WATCH_W + 20, WATCH_H, ge, 1, WHITE, BLACK, 32);
        LCD_ShowChar(WATCH_W + 40, WATCH_H - 2, ':', WHITE, BLACK, 32, 0);	
    }
    /* day */
    enum_tmp = (month_enum_t)(p_time.mon[1] * 10 + p_time.mon[0]);
    tmp = r_time.day + p_time.day[1]*10 + p_time.day[0];
    if(enum_tmp == JAN || enum_tmp == MAR || enum_tmp == MAY || enum_tmp == JUL || enum_tmp == AUG ||
            enum_tmp == OCT || enum_tmp == DEC)
    {
        if((tmp % 31) == 0)
        {
            r_time.mon = tmp / 31;
            r_time.update.bit.umon = UPDATE_ENABLE;
        }
        tmp %= 31;
    }
    else if(enum_tmp == APR || enum_tmp == JUN || enum_tmp == SEP || enum_tmp ==NOV)
    {
        if((tmp % 30 == 0))
        {
            r_time.mon = tmp / 30;
            r_time.update.bit.umon = UPDATE_ENABLE;
        }
        tmp %= 30;
    }
    else
    {
        if((tmp % 28 == 0))
        {
            r_time.mon = tmp / 28;
            r_time.update.bit.umon = UPDATE_ENABLE;
        }
        tmp %= 28;
    }
    ge = tmp % 10;
    shi = tmp / 10;
    if(r_time.update.bit.uday)
    {
        r_time.update.bit.uday = UPDATE_DISABLE;
        LCD_ShowIntNum(WATCH_W + 120, WATCH_H - 25, shi, 1, WHITE, BLACK, 24);
        LCD_ShowIntNum(WATCH_W + 135, WATCH_H - 25, ge, 1, WHITE, BLACK, 24);
    }
    /* mon */
    tmp = r_time.mon + p_time.mon[1]*10 + p_time.mon[0];
    if((tmp % 12) == 0)
    {
        r_time.year = tmp / 12;
        r_time.update.bit.uyear = UPDATE_ENABLE;
    }
    tmp %= 12;
    ge = tmp % 10;
    shi = tmp / 10;
    if(r_time.update.bit.umon)
    {
        r_time.update.bit.umon = UPDATE_DISABLE;
        LCD_ShowIntNum(WATCH_W + 75, WATCH_H - 25, shi, 1, WHITE, BLACK, 24);
        LCD_ShowIntNum(WATCH_W + 90, WATCH_H - 25, ge, 1, WHITE, BLACK, 24);
        LCD_ShowChar(WATCH_W + 105, WATCH_H - 20, '/', WHITE, BLACK, 16, 0);
    }

    /* year */
    tmp = r_time.year + p_time.year[3] * 1000 + p_time.year[2] * 100 + p_time.year[1] * 10 + p_time.year[0];
    ge = tmp % 10;
    shi = tmp / 10 % 10;
    bai = tmp / 100 % 10;
    qian = tmp / 1000 % 10;
    if(r_time.update.bit.uyear)
    {
        r_time.update.bit.uyear = UPDATE_DISABLE;
        LCD_ShowIntNum(WATCH_W, WATCH_H - 25, qian, 1, WHITE, BLACK, 24);
        LCD_ShowIntNum(WATCH_W + 15, WATCH_H - 25, bai, 1, WHITE, BLACK, 24);
        LCD_ShowIntNum(WATCH_W + 30, WATCH_H - 25, shi, 1, WHITE, BLACK, 24);
        LCD_ShowIntNum(WATCH_W + 45, WATCH_H - 25, ge, 1, WHITE, BLACK, 24);
        LCD_ShowChar(WATCH_W + 60, WATCH_H - 20, '/', WHITE, BLACK, 16, 0);	
    }
    /* wday */
    enum_tmp = (wday_enum_t)p_time.wday;
    if(r_time.update.bit.uwday)
    {
        r_time.update.bit.uwday = UPDATE_DISABLE;
        switch(enum_tmp)
        {
            case SUN:
                LCD_ShowChinese(WATCH_W + 150, WATCH_H - 25, (uint8_t*)"星期天", WHITE, BLACK, 24, 0);
                break;
            case MON:
                LCD_ShowChinese(WATCH_W + 150, WATCH_H - 25, (uint8_t*)"星期一", WHITE, BLACK, 24, 0);
                break;
            case TUES:
                LCD_ShowChinese(WATCH_W + 150, WATCH_H - 25, (uint8_t*)"星期二", WHITE, BLACK, 24, 0);
                break;
            case WED:
                LCD_ShowChinese(WATCH_W + 150, WATCH_H - 25, (uint8_t*)"星期三", WHITE, BLACK, 24, 0);
                break;
            case THUR:
                LCD_ShowChinese(WATCH_W + 150, WATCH_H - 25, (uint8_t*)"星期四", WHITE, BLACK, 24, 0);
                break;
            case FRI:
                LCD_ShowChinese(WATCH_W + 150, WATCH_H - 25, (uint8_t*)"星期五", WHITE, BLACK, 24, 0);
                break;
            case SAT:
                LCD_ShowChinese(WATCH_W + 150, WATCH_H - 25, (uint8_t*)"星期六", WHITE, BLACK, 24, 0);
                break;
            default:
                break;
        }
    }
 }	

时间校准代码

import serial
from datetime import datetime

ser = serial.Serial("COM8", 115200, *timeout*=1)  # 这里有三个参数,第一个是连接的端口,第二个是波特率,第三个是超时时间
now = datetime.now() # 获取当前datetime
now_data = now.strftime('%Y-%m-%d %H:%M:%S %w')
print(now_data)
if ser.isOpen():
  print("串口已打开")
else:
  print("串口未打开")
# 测试
send_data = 0x01
# ser.write(send_data.encode())
print(now_data.encode())
# 发送数据
ser.write(now_data.encode())
ser.close()
if ser.isOpen():
  print ('串口未关闭')
else:
  print ('串口已关闭')

心率和血样检测代码

void blood_data_update(void)
{
	fifo_t raw_data;
	uint16_t temp_num = 0;
	char buf[100] = {0};
	uint8_t index = 0;
	temp_num = max30100_bus_read(INTERRUPT_STATUS_REG);
    //标志位被使能时 读取FIFO
	if(INTERRUPT_STATUS_REG_A_FULL & temp_num)
	{
		//读取FIFO
		max30100_fifo_readBytes(FIFO_DATA_REG, &raw_data, 1); //read the hr and spo2 data form fifo in reg=0x05
//		index = sprintf(buf, "%d", raw_data.rawIR);
//		print_data((uint8_t*)buf, index);
		
		/* 直流滤波 */
		blood.dcFilterIR = dcRemoval((float)raw_data.rawIR, blood.dcFilterIR.w, ALPHA);
		blood.dcFilterRed = dcRemoval((float)raw_data.rawRed, blood.dcFilterRed.w, ALPHA);
//		index = sprintf(buf, "%.6f", dcFilterIR.result);
//		print_data((uint8_t*)buf, index);
		
		/* 均值中值滤波 */
		float meanDiffResIR = meanDiff(blood.dcFilterIR.result, &blood.meanDiffIR);
		float meanDiffResRed = meanDiff(blood.dcFilterRed.result, &blood.meanDiffRed);
//		index = sprintf(buf, "%.6f", meanDiffResIR);
//		print_data((uint8_t*)buf, index);
		
		/* 巴特沃斯过滤器 */
		lowPassButterworthFilter(meanDiffResIR, &blood.lpbFilterIR);
		lowPassButterworthFilter(meanDiffResRed, &blood.lpbFilterRed);
		
		blood.irACValueSqSum += blood.dcFilterIR.result * blood.dcFilterIR.result;
		blood.redACValueSqSum += blood.dcFilterRed.result * blood.dcFilterRed.result;
//		index = sprintf(buf, "%.6f", blood.lpbFilterIR.result);
//		buf[index] = ',';
//		index += 1;
//		index += sprintf(&buf[index], "%.6f", blood.lpbFilterRed.result);
//		print_data((uint8_t*)buf, index);
		
		blood.samplesRecorded++;
		/* 心跳脉冲检测 */
		if(detectPulse(blood.lpbFilterIR.result))
		{
			blood.updateDisplay = BLOOD_UPDATE_DISPLAY_ENABLE;
			blood.pulsesDetected++;
			float red_log_rms = log(sqrt(blood.redACValueSqSum / blood.samplesRecorded));
			float ir_log_rms = log(sqrt(blood.irACValueSqSum / blood.samplesRecorded) );
			float ratioRMS = 0.0f;
			if(red_log_rms != 0.0f && ir_log_rms != 0.0f)
			{
				ratioRMS = red_log_rms / ir_log_rms;
		    }
			blood.currentSaO2Value = 110.0f - 14.0f * ratioRMS;	// SPo2 value by pulse-rate
//			index = sprintf(buf, "%.6f", blood.currentSaO2Value);
//			print_data((uint8_t*)buf, index);
			if(blood.pulsesDetected % RESET_SPO2_EVERY_N_PULSES == 0)
			{
				blood.irACValueSqSum = 0;
				blood.redACValueSqSum = 0;
				blood.samplesRecorded = 0;
			} 
		} 
		balanceIntesities(blood.dcFilterRed.w, blood.dcFilterIR.w);
	}
}

3.心得体会

这次活动,第一让我对SPI、I2C以及UART又复习一遍,对照手册的说明,了解这些接口还有其它许多功能是我以前是没有使用过的,这次借此机会重新对这些接口有了更深认识。平时使用意法半导体开发都是使用STM32CUBEMX一键生成,省去很多偏向底层的东西,但美信半导体MAX32660控制器让我重新复习一遍底层知识。第二点是使用了美信半导体MAX30100传感器来检测心率和血氧浓度,数据处理对我种小白来说还挺难的,也不知道如何去处理这些数据,最后参考网上一篇博文,并用串口工具VOFA+去显示波形来直观分析数据,血氧浓度检测还行,但心跳检测欠佳。

最后,感谢硬禾学堂和得捷电子,让我接触到了美信半导体的开发板,让我能在空余时间能更多参与有趣项目学习,也感谢群的小伙伴提供很多种实现题目功能的思路,感谢大家一路的折腾与陪伴,谢谢!

 

附件下载
02-代码.zip
团队介绍
电子爱好者
团队成员
洪志军
爱折腾的电子小白
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号