内容介绍
内容介绍
1.功能实现
本代码主要实现了日期星期和时间显示以及心率和血氧浓度的检测功能。
2.硬件连接

使用了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
查看更多
猜你喜欢
Funpack第六期-MAX32660-EVSYS-心率计利用 MAX32660 作为主控 MCU,MAX30102 传感器采样数据,SSD12864 显示,完成了一个心率计的功能。
Jackistang
2357
Funpack第六期--基于MAX32660-EVSYS设计的具有计步和指南针功能的手表本项目使用MAX32660作为主控制作电子手表,具有日常日期时间显示,具有计步,温度,指南针等功能。
振青666
1940
Funpack第六期 MAX32660-EVSYS 心率检测及时钟显示Funpack第六期活动,用MAX32660-EVSYS、LCD、MAX30102实现心率检测及时钟显示
willcome0
2183