SEGA星空投影仪太贵买不起?教你用STM32自制星空灯!

SEGA星空投影仪太贵买不起?教你用STM32自制星空灯!

女神最近种草了一款星空投影仪——日本SEGA的Homestar。白柴看着少则几百,多则上千的价格,不禁感觉钱包一紧

再看看某宝上几十块的星空灯,功能不少,但图案偏幼稚,一点都不浪漫。这样的东西,如何拿得出手送给女神呢?

看来只能自己动手DIY了。鲁迅曾说过:“如果没有你想要的星空灯,那么就自己做一个。”

事不宜迟,立马打电话给大Z老师,向他学习星空灯的制作。现在白柴就把整理好的制作方法分享给大家,感兴趣的粉丝们可以跟着一起做哟~

星空灯DIY报名征集

看完已心动,自己也想做?

现在报名参加EEPW星空灯DIY内测活动,满足你的愿望!

报名时间:4.23~4.30

参与名额:10名

*内测活动,活跃度高的粉丝更容易被选中

亮点1

100元开发补贴 人人都有

放飞创意 费用我们报销

亮点2

自己设计灯罩 我的灯 我做主

实现星空灯自由

亮点3

记录过程 参与评比 得分最高者

我们将在520当天送上一份花束大礼(价值200元)

带上鲜花和星空灯,去和她告白吧!

如何报名?

添加小助手微信(ID:baichai1025),私信【星空灯报名】。

收到小助手回复后,即为报名成功。

扫一扫,添加小助手

☝星空灯成品展示

效果超nice!绝对能感动女神

物料清单

本体:

STM32最小系统板(1个)

WS2812灯带(1个)

HC-06蓝牙模块(1个)

杜邦线若干

星空灯本体

灯罩:

裁好的亚克力板(1份)

黑色墙纸(适量)

灯罩裁切图纸

制作详解

虽然叫星空灯,但本体实际是STM32点流水灯。硬件不复杂,属于入门级,有一点单片机和编程基础的朋友都可尝试制作。本文只给出了基础电灯程序,欢迎技术大神们开发添加新功能。

01

WS2812彩灯

先简单说下WS2812的工作过程:

1)上电时,WS2812数据IO为低电平保持。

2)空闲时,IO为低电平。

3)数据发送完毕后,保持电平,超过规格书上定义的RESET时间(只有低电平时间超过280us,就可以认为是RESET)。

接下来,我们看下数据的时序,如图1。

图1 数据的时序

如图2,是每24bit的组成。注意,顺序不是RGB888,而是GRB888。一般我们取颜色的数值,都是RGB顺序,所以这里在代码里实现的时候,会需要做一下移位。另外,需要注意的是,需要高位先发(MSB)。

图2 24bit数据结构

图3是时序波形图。关键在于用什么方法去表示Bit的波形,网络上的方法有很多。例如PWM,也有用SPI。今天我们就先用一种简单方法实行吧,IO口模拟是不错的选择。

图3 时序波形图

现在我们开始配置工程吧。打开STM32CubeMX,新建一个工程,图4。

图4

在搜索框内,搜索我们的开发板型号,也就是STM32G070RB,图5。这样工程就新建好了。

图5

配置时钟树,如图6所示。

图6

接下来配置引脚 ,这里我们采用的是PWM+DMA的方式来驱动WS2812。通过WS2812的手册,可以得知驱动需要800KHZ的频率。现在我们来配置定时器,这里以定时器1为例来配置。如图7所示,图中的Pulse是指一个周期的脉冲数;计算方法79=(64M/800K)-1得出。

图7

下面我们开始配置DMA,如图8所示配置。

图8

到这里就可以生成代码了,图9。

图9

我们打开工程,将下面的代码,复制到图10所在位置:

#define ONE_PULSE (59) //1 码计数个数#define ZERO_PULSE (29) //0 码计数个数#define RESET_PULSE (80) //80 复位电平个数(不能低于40)#define LED_NUMS (12) //led 个数#define LED_DATA_LEN (24) //led 长度,单个需要24个字节#define WS2812_DATA_LEN (LED_NUMS*LED_DATA_LEN) //ws2812灯条需要的数组长度uint16_t static RGB_buffur[RESET_PULSE +WS2812_DATA_LEN] = { 0 };

图10

接下来就是DMA传输完成回调函数(根据你使用的定时器配置),以下函数都复制到main.c 的/* USER CODE BEGIN 4 */代码区:

voidHAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim){ HAL_TIM_PWM_Stop_DMA(&htim1,TIM_CHANNEL_1);}

下面就是WS2812的驱动函数了,该函数的作用是根据WS2812的数量将灯的GRB颜色数据写到需要DMA传送的数组中:

void WS281x_SetPixelColor(uint16_t n,uint32_t GRBColor){ uint8_ti; if(n < LED_NUMS) { for(i = 0; i < 24; ++i) RGB_buffur[24* n + i] = (((GRBColor << i) & 0X800000) ? ONE_PULSE : ZERO_PULSE); }}

将三个颜色的数据合并成GRB数据:

uint32_t WS281x_Color(uint8_t red, uint8_tgreen, uint8_t blue){ returngreen << 16 | red << 8 | blue;}

这是一个简单的颜色渐变算法 ,感兴趣的可以研究研究:

uint32_t Wheel(uint8_t WheelPos){ WheelPos= 255 - WheelPos; if(WheelPos < 85) { returnWS281x_Color(255 - WheelPos * 3, 0, WheelPos * 3); } if(WheelPos < 170) { WheelPos-= 85; returnWS281x_Color(0, WheelPos * 3, 255 - WheelPos * 3); } WheelPos-= 170; returnWS281x_Color(WheelPos * 3, 255 - WheelPos * 3, 0);}

这里简单的写了两个演示程序:

void Mode2_LED(uint8_t wait){ uint32_ttimestamp = HAL_GetTick(); uint16_ti; staticuint8_t j; staticuint32_t next_time = 0; uint32_tflag = 0; if(next_time < wait) { if((uint64_t)timestamp + wait - next_time > 0) flag= 1; } elseif (timestamp > next_time) { flag= 1; } if(flag) { j++; next_time= timestamp + wait; for(i = 0; i < LED_NUMS; i++) { WS281x_SetPixelColor(i,Wheel((i + j) & 255)); } } HAL_TIM_PWM_Start_DMA(&htim1,TIM_CHANNEL_1,(uint32_t*)RGB_buffur,RESET_PULSE + WS2812_DATA_LEN);}

void Mode1_LED(uint8_t wait){ uint32_ttimestamp = HAL_GetTick(); uint16_ti; staticuint8_t j; staticuint32_t next_time = 0; staticuint8_t loop = 0; if(loop == 0) next_time= timestamp; loop= 1; //首次调用初始化 if((timestamp > next_time)) // && (timestamp - next_time < wait*5)) { j++; next_time= timestamp + wait; for(i = 0; i < LED_NUMS; i++) { WS281x_SetPixelColor(i,Wheel(((i * 256 / (LED_NUMS)) + j) & 255)); } } HAL_TIM_PWM_Start_DMA(&htim1,TIM_CHANNEL_1,(uint32_t*)RGB_buffur,RESET_PULSE + WS2812_DATA_LEN);}

在主函数中直接调用Mode1_LED和Mode2_LED函数即可。

OK,点亮之后相当炫酷。大家可以借鉴,修改出自己独特的风格。

02

按键模式切换

这里简单的写了几种模式,图11。

图11

通过图12的原理图可以看出来,开发板上的按键连接的是PC13引脚。

图12

好的,知道了按键的引脚,我们打开STM32CubeMX软件来配置引脚,如图13所示。

图13

同时使能中断,如图14。

图14

生成代码,打开工程。

在/* USER CODE BEGIN 4 */

/* USER CODE END 4 */之间重写中断回调函数,代码如下。

void HAL_GPIO_EXTI_Rising_Callback(uint16_tGPIO_Pin){ switch(GPIO_Pin) { caseGPIO_PIN_13:Mode_Led++; if(Mode_Led == 'I') Mode_Led ='A';break; }}

通过主函数判断当前的模式,并运行相对应的模式实现函数,如图15。

图15

通过按键进行模式选择就完成了。

03

蓝牙控制

接下来,用手机通过蓝牙来控制WS2812的模式和速度调节。

先打开STM32CubeMX配置我们要用到串口(这里以串口2为例,大家根据自己的喜好选择)。

因为蓝牙模块默认是9600Bits/s,所以我们配置时要与蓝牙的波特率一致,如下图16所示。

图16

同时我们使能下串口的收发中断,如图17。

图17

这样串口就配置好了,点击生成代码,打开工程。

图18

我们可以看到串口2的初始化程序,图18。

接下来添加代码,定义一个字符的数据接收,图19。

图19

添加以下代码,打开串口接收中断,图20。

图20

重写串口接收中断回调函数, 代码如下:

voidHAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){ if(Buffer>='A' && Buffer <='H') { Mode_Led = Buffer; printf("灯光模式:%c",Mode_Led); } if(Buffer == 'I') { Mode_wait +=10; if(Mode_wait==500) Mode_wait=490; printf("灯光速度:%d",500-Mode_wait); } if(Buffer == 'J') { Mode_wait -=10; if(Mode_wait==40) Mode_wait=50; printf("灯光速度:%d",500-Mode_wait); } HAL_UART_Receive_IT(&huart2,&Buffer, 1);}

这里的printf对串口发送函数进行了重定义,方法如下:

int fputc(int c, FILE *stream) //重写fputc函数{ /* huart1是工具生成代码定义的UART1结构体, 如果以后要使用其他串口打印,只需要把这个结构体改成其他UART结构体。*/ HAL_UART_Transmit(&huart2, (unsigned char *)&c, 1, 1000); return 1;}

将代码复制到

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */之间。

接下来点击Options for Target,把Use MicroLIB打上对勾,图21。

图21

至此就完成了对串口发送函数的重写,可以正常的使用printf函数了。我们可以通过输出一定的信息,显示出当前的状态。

连上蓝牙模块,发送模式对应的字符,就可以实现模式的切换,以及实现调速,图22。

图22

到这里,我们就实现了使用蓝牙控制的效果。

感兴趣粉丝们,欢迎报名参加我们的DIY活动哟~

扫码入群

扫码添加管理员微信

加入“电子产品世界”粉丝交流群

相关推荐

bt365备用网站 Mac桌面壁纸软件

Mac桌面壁纸软件

📅 07-04 👁️ 6101
365bet亚洲投注 巨齿鲨的完整骨头简笔画简单(推荐20张)
365bet亚洲投注 「轻尘车评」8级最强输出机器——“天蝎”