心跳跟随的心形灯(STM32(HAL)+WS2812+MAX30102)

文章目录


前言

在好几年前,我好像就看到了焊武帝 jiripraus在纪念结婚五周年时,制作的一个心跳跟随心形灯,感觉太浪漫了,于是在假期的时候,我也仿照做了一个,虽然还有很多需要完善的地方,但是大致功能已经实现了,下面开源讲讲开源的项目。




介绍

系统框架

心脏的外壳采用紫铜丝或黄铜丝焊接,1mm的铜丝较硬,适合完成外部框架的搭建,0.7mm的铜丝可塑性较好,适合焊接内部的WS2812,整个过程中吗,焊接难度较大,电子器件的固定难度也很大,我内部借助了一些热熔胶固定,有些丑,可以参考 jiripraus的固定方法。

原作者采用的Arduino主控,且没有借助PCB,纯飞铜线完成内部电子器件的连接。

因为我想利用FreeRTOS学习一下实际项目编写,所以将主控更换成了STM32F103C8T6,当然编写了两套代码,先编写了一套裸机开发,测试能够完成所有功能、之后移植了一套FreeRTOS的,都可以运行。

原项目地址

jiripraus项目地址(Arduino):https://www.instructables.com/Beating-LED-Heart/

本项目开发开源地址

裸机开发软件下载(STM32版本):https://download.csdn.net/download/zerokingwang/88173336

FreeRTOS开发软件下载(STM32版本):https://download.csdn.net/download/zerokingwang/88173410

硬件PCB(立创开源)下载链接:https://oshwhub.com/zeroking/my_heart_

所有文件上传到github了,也可以到github下载:https://github.com/VioletJA/MY_HEART

硬件PCB

绘制了包括STM32F103C8T6的最小系统、蜂鸣器、TP4059锂电池充电电路、OLED接口、MAX30102接口等。

软件功能

在没有手指触碰到MAX30102模块时,WS2812进行彩虹灯变换,当手指触碰到时,跟随检测到的心跳进行红色闪烁及蜂鸣器模拟心跳。

详细内容

硬件外壳制作

在3D打印的器件上进行焊接,我先用胶带固定好铜丝,然后再各个连接点进行焊接。

心形3D打印件:https://www.cgtrader.com/items/865899/download-page

WS2812焊接:最好黏在胶带上,固定好大致位置后再进行焊接,我没有找到其他好方法,焊接极其耗时。

WS2812级联及控制

当发送一串数据时,第一个24bits的数据给D1,第n个24bits的数据给第n。(24bit对应三原色,每一色八位)

输出码型通过下图判断,因此每一位的高低都需要进行严格控制

大多数采用的是PWM+DMA的模式,通过传输一串数据,控制不同占空比的PWM完成数据传输

c 复制代码
HAL_TIM_PWM_Start_DMA(&ws2812_TIM, ws2812_CHANNEL, (uint32_t *)Pixel_Buf,(Pixel_NUM+1)*24);

参考:STM32系列(HAL库)------F103C8T6驱动WS2812全彩RGB模块(PWM+DMA方式)

但有一点需要注意,必须在PWM的完成回调函数中进行手动关闭DMA传输,否则WS2812颜色不对(应该就是DMA传输出错了)

c 复制代码
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==ws2812_TIM.Instance)
	HAL_TIM_PWM_Stop_DMA(&ws2812_TIM, ws2812_CHANNEL);
}

其他详细的代码控制都在开源代码中了

MAX30102血氧传感器

参考:【stm32】手把手用cubemx配置血氧传感器(MAX30102)

采用的是中断处理,每次模块发送一个下降沿中断,STM32接收到后进行数据处理。

模块采用IIC进行控制与数据读取,普通采用HAL配置硬件IIC就能够实现,但是在我用STM32F103C6T6进行测试的时候,HAl生成的代码中,IIC引脚不会自动设置为高速,导致运行会出现HAL_ERROR的问题,然而在STM32F103C8T6就已经纠正了这个错误。

c 复制代码
void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(i2cHandle->Instance==I2C1)
  {
    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    __HAL_RCC_I2C1_CLK_ENABLE();
  else if(i2cHandle->Instance==I2C2)
  {
    __HAL_RCC_GPIOB_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    __HAL_RCC_I2C2_CLK_ENABLE();
  }
}

0.96OLED

在屏幕调试中,由于1616的字看上去太小了,于是我调整为2424的大小,这个时候,网上的一些库就不够用了,需要看懂是如何完成一个汉字的显示。

利用PCtoLCD2002完成取字模,一般设置如下

数据如下:

c 复制代码
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x08,0x10,0x70,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x80,0xC0,0xF8,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xC0,0x04,0x18,0xF0,0xE0,0x00,0x00},
{0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x1F,0x18,0x10,0x10,0x10,0x10,0x10,0x10,0x18,0x1F,0x08,0x00,0x00,0x00,0x00,0x00},/*"心",0*/

网上一般说取数据是按照一行一行来的,一个数据代表的是8位,不足8位的按照8位算。

但是我实际测试,是按列算的,例如第一个数据0x00,表示第一列中的前八个点全没有点亮,第二个0x00代表的是第二列数据的前8个也全部没有点亮,当前8行的所有列都完成后,计算中间8行,一列一列,之后计算下面8行(24*24的数据)

例如下图:

而我们采用的低位在前时:

当第一个格子点亮时,对应的就是0x01

在OLED汉字显示的函数中,需要将汉字分为三上中下,完成三次打印,组成一个完整的汉字

c 复制代码
//显示24*24汉字
void OLED_ShowCHinese24(uint8_t x,uint8_t y,uint8_t no)
{      			    
	uint8_t t;
	OLED_Set_Pos(x,y);	
    for(t=0;t<24;t++)
	{
		OLED_WR_Byte(CHI_24_24[3*no][t],OLED_DATA);
     }	
	OLED_Set_Pos(x,y+1);	
    for(t=0;t<24;t++)
	{	
		OLED_WR_Byte(CHI_24_24[3*no+1][t],OLED_DATA);
     }
	OLED_Set_Pos(x,y+2);	
    for(t=0;t<24;t++)
	{	
		OLED_WR_Byte(CHI_24_24[3*no+2][t],OLED_DATA);
     }	
}

FreeRTOS

在移植操作系统的时候,创建了两个任务,一个完成WS2812和蜂鸣器的控制,一个完成MAX30102和OLED的控制。

其中出现了一些跨任务的信号量,尝试利用任务通知方式进行数据保护,但是效果不佳,最后还是利用了临界区保护,在信号改写的地方进行了保护(临界区时间太长,系统会崩溃)

效果视频

心跳跟随心形灯

总结

通过制作这个小项目,还是学习了HAL的IIC配置,PWM的DMA模式,WS2812的控制,OLED屏幕控制,FreeRTOS也理解更多了一点,当然还只是刚开始入门,要学的东西还是有很多,在这个小项目中,还有很多bug和不完善的地方,比如说MAX30102测量的心率不准等等,但是目前打算就先这样结束,先继续学习吧。

相关推荐
honey ball2 小时前
LLC与反激电路设计【学习笔记】
单片机·嵌入式硬件·学习
Graceful_scenery8 小时前
STM32F103外部中断配置
stm32·单片机·嵌入式硬件
猿来不是梦11 小时前
RT_Thread内核源码分析(三)——线程
嵌入式硬件·系统架构·rt_thread操作系统
白书宇11 小时前
19.QT程序简单的运行脚本
linux·arm开发·嵌入式硬件·物联网·arm
我不是板神12 小时前
嵌入式MCU常见问题分类汇总
c语言·stm32
Arciab12 小时前
51单片机入门:独立按键(02)
单片机
大梦百万秋13 小时前
嵌入式系统与单片机工作原理详解
单片机·嵌入式硬件
陌夏微秋13 小时前
硬件基础22 反馈放大电路
单片机·嵌入式硬件·硬件架构·硬件工程·智能硬件
&春风有信13 小时前
FreeRTOS之链表源码分析
c语言·数据结构·嵌入式硬件·链表
美式小田14 小时前
Altium Designer学习笔记 21.PCB板框的评估及叠层设置
笔记·嵌入式硬件·学习·ad