Funpack第六期 MAX32660-EVSYS 心率检测及时钟显示
Funpack第六期活动,用MAX32660-EVSYS、LCD、MAX30102实现心率检测及时钟显示
标签
嵌入式系统
willcome0
更新2021-04-01
1047
MAX32660-EVSYS 心率检测及时钟显示

MAX32660-EVSYS介绍

FoG8MC5whgxKLFApckZwU4jw6zhj

MAX32660 微控制器

· Arm Cortex-M4内核, 工作频率96MHz
· 256KB Flash Memory
· 96KB SRAM
· 16KB 指令缓存

MAX32660最小系统部分

· DIP双排通孔设计,孔间距2.54mm
· 可支持面包板
· 一个用户LED
· 一个用户按键

基于MAX32625PICO调试功能部分

· 支持CMSIS DAP SWD调试器
· 一路虚拟串口

MAX32660,超低功耗、高成效、高度集成微控制器

该评估板核心为 MAX32660 微控制器,属于美信 DARWIN 产品系列,是一款超低功耗、性价比突出、集成度非常高的32位控制器。芯片封装非常小,4mm x 4mm 的 TQFN 已经是这个系列里最大封装,非常适合电池供电或是无线传感器的应用。

MAX32660 采用了带浮点运算功能的 Cortex-M4 内核,最大主频96MHz,带256KB Flash和96KB SRAM,在如此小的体积下,性能还是比较强的。

应用场景:

· 便携式医疗设备
· 运动手表
· 可穿戴医疗设备

引脚功能:

FhjxN4JHtq5Q5eMERF9AQ9upu0-r 

硬件连接

LCD:
    SCL -> P0_6 (SPI0A_SCK)
    SDA -> P0_5 (SPI0A_MOSI)
    RES -> P0_9 (GPIO)
    SDA -> P0_8 (GPIO)

MAX30102:
    SCL -> P0_2 (IIC1A_SCL)
    SDA -> P0_3 (IIC1A_SDA)

UART:
    TXD -> P0_10 (UART1A_TX)
    RXD -> P0_11 (UART1A_RX)

实现功能

系统框架

使用了 rt-thread 实时操作系统,这里在默认 BSP 配置下开启 SPI0 及 I2C1。同时关闭了软件模拟 I2C 来解决一个 BSP 生成工程时报的缺少文件错误。

Fmi8UdTRxrinlvuL7Ne_H-x1EWhU

同时编写配置文件,增加写好的LCD驱动与MAX30102驱动

Import('RTT_ROOT')
Import('rtconfig')
from building import *

cwd = GetCurrentDir()

src = Split('''
drv_lcd.c
drv_max30102.c
''')

path =  [cwd]
group = DefineGroup('Device', src, depend = [''], CPPPATH = path)
Return('group')

时钟显示

通过调用 rt_tick_get_millisecond() 函数,来获取当前系统已运行的时间。结合初始设定的时间,即可算出当前时间。

// 初始时间定义
#define HOUR   20
#define MINUTE 59
#define SECOND 59

void ClockProcess(void)
{
    uint32_t sec = rt_tick_get_millisecond() / 1000;
    sec += HOUR * 3600 + MINUTE * 60 + SECOND;
    
    uint8_t s = sec % 60;
    uint8_t m = (sec / 60) % 60;
    uint8_t h = (sec / 60 / 60) % 24;

    lcd_show_string(10, 100, 32, "%02d:%02d:%02d", h, m, s);
}

LCD显示驱动

LCD显示驱动移植自 RT-Thread 潘多拉 STM32L475 中 LCD 例程,使用的是SPI0,这里不过多介绍。

static int rt_hw_lcd_init(void)
{
    rt_hw_spi_device_attach("spi0", "spi00", LCD_CS_PIN);
    lcd_gpio_init();
    /* Memory Data Access Control */
    lcd_write_cmd(0x36);
    lcd_write_data(0x00);
    /* RGB 5-6-5-bit  */
    lcd_write_cmd(0x3A);
    lcd_write_data(0x65);
    /* Porch Setting */
    lcd_write_cmd(0xB2)
    ...
}

心率测量

static struct rt_i2c_bus_device *i2c_dev;

static void write_reg(uint8_t addr, uint8_t data)
{
    uint8_t buf[] = {addr, data};
    struct rt_i2c_msg send_msg = {0};

    send_msg.addr = MAX30102_ADDR;
    send_msg.flags = RT_I2C_WR;
    send_msg.buf = buf;
    send_msg.len = sizeof(buf);

    rt_i2c_transfer(i2c_dev, &send_msg, 1);
}

static void read_reg(uint8_t addr, uint8_t *data, uint16_t len)
{
    struct rt_i2c_msg send_msg[2] = {0};
    
    send_msg[0].addr = MAX30102_ADDR;
    send_msg[0].flags = RT_I2C_WR;
    send_msg[0].buf = &addr;
    send_msg[0].len = 1;

    send_msg[1].addr = MAX30102_ADDR;
    send_msg[1].flags = RT_I2C_RD;
    send_msg[1].buf = data;
    send_msg[1].len = len;

    rt_i2c_transfer(i2c_dev, send_msg, 2);
}

void max30102_init(void)
{
    i2c_dev = (struct rt_i2c_bus_device *)rt_device_find("i2c1");
    if (i2c_dev == RT_NULL) {
        rt_kprintf("Err: Not find i2c1.");
        return;
    }

    write_reg(REG_INTR_ENABLE_1, 0xc0);
    write_reg(REG_INTR_ENABLE_2, 0x00);
    write_reg(REG_FIFO_WR_PTR, 0x00);
    write_reg(REG_OVF_COUNTER, 0x00);
    write_reg(REG_FIFO_RD_PTR, 0x00);
    write_reg(REG_FIFO_CONFIG, 0x4f);
    write_reg(REG_MODE_CONFIG, 0x03);
    write_reg(REG_SPO2_CONFIG, 0x27);
    write_reg(REG_LED1_PA, 0x24);
    write_reg(REG_LED2_PA, 0x24);
    write_reg(REG_PILOT_PA, 0x7f);
}

void max30102_read_fifo(uint32_t *red_data, uint32_t *ir_data)
{
    uint8_t temp = 0;
    uint8_t buf[6] = {0};

    read_reg(REG_INTR_STATUS_1, &temp, 1);
    read_reg(REG_INTR_STATUS_2, &temp, 1);
    read_reg(REG_FIFO_DATA, buf, sizeof(buf));

    *red_data = ((uint32_t)buf[0] << 16) + ((uint32_t)buf[1] << 8) + buf[2];
    *ir_data  = ((uint32_t)buf[3] << 16) + ((uint32_t)buf[4] << 8) + buf[5];
}

通过调用 max30102_read_fifo() 获取红光测量值,并使用前项差分法对数据进行处理,即将新数据减去上次获取的数据。 通过输出成波形可以比较清楚地发现每次向下的尖峰即为一次心跳。(好文章: https://mp.weixin.qq.com/s/RhFktwAu6L0a6wkFoBRm0A)

FlXu7InpXoTdwqrJhCibm7Ak9B5F

因此这里设定了一个固定阈值 FINGER_PRESS_MIN,当红光数据小于该值认为手指按下;并当红光差分数据小于 -500 时,认为检测到一次心跳,且要再次检测 15 个数据之后,才能检测下一次心跳,来避免连续判断同一个尖峰区间为一次心跳,具体代码如下。

if (red_data > FINGER_PRESS_MIN) { // 手指按下
    
    rt_uint16_t last_beat_num = beat_num;
    static rt_uint32_t measure_time = 0;
    if (diff_data < -500 && count > 15) { // 判断到一次心跳
        count = 0;
        if (beat_num == 0) {
            lcd_clear(BLACK);
            lcd_show_string(50, 210, 24, "Measuring...");
        }
        
        if (beat_num++ == 5) {
            measure_time = rt_tick_get_millisecond();
            lcd_fill(0, 210, 240, 240, BLACK);
            lcd_show_string(10, 210, 24, "beat:");
            lcd_show_string(135, 210, 24, "rate:");
        }
        
        if (beat_num < 5) {
            continue;
        }
        
        // 计算心率
        heart_rate = (rt_uint32_t)(beat_num - 5) * 1000 * 60 / (rt_tick_get_millisecond() - measure_time);

        // 输出心跳数及心率到屏幕
        if (last_beat_num != beat_num && beat_num > 5) {
            lcd_show_string(80, 210, 24, "%-3d ", beat_num - 5);
            lcd_set_color(BLACK, RED);
            lcd_show_string(195, 210, 24, "%-3d", heart_rate);
            lcd_set_color(BLACK, WHITE);
        }
    }
}

同时还将得到的差分数据在LCD上实时进行倒置输出,可以得到一个类似心电图的跳动趋势图:

FqQi1J_-Fi7N7uWD8PN3EZygpeiF

代码实现如下:

static void draw_heart_beat(rt_int16_t data)
{
    static rt_uint8_t time = 0;
    time += 2;
    
    if (time >= 239) {
        lcd_fill(0, 0, 240, 205, BLACK);
    }
    time = time % 240; 
    data = (data + 2500) < 0 ? 0 : (data + 2500);
    data /= 15;
    data = data < 5 ? 5 : (data > 200 ? 200 : data); 
    lcd_draw_point(time, data);
}

心得体会

第一次用资源这么紧凑的MCU,引脚能复用成多种功能,各种主流接口基本都有,有些超乎到手前的想象。几年前草草地做过心率传感器的项目,但涉及心率检测这块用的别人的代码一直没怎么理解,恰逢推送了相关的文章,能重写当时不会的东西,确实感到挺高兴。

附件下载
rt-thread_max32660.rar
已删除rt-thread多余部分
团队介绍
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号