完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
想法来源:笔者之前逛某宝时候,发现有个贴在电脑屏幕四周的灯带,并且灯带会随着电脑屏幕颜色变化而变化,当时就在心中就埋下了DIY的想法,直到后来帮老师做东西,得到了一些边角料,于是下决心干它!
软件 下位机 本次采用WS2812灯带,所以首先要将其驱动点亮。 于是查阅WS2812的数据手册,其驱动时序如下图。 网上有很多驱动方法,有SPI、PWM、定时器等,但是本次笔者采用最简单的IO翻转来驱动它,上面提到其他方法请读者自行搜索查阅。 #ifndef __WS2812_H #define __WS2812_H #include “sys.h” #define LED_Nums 60 #define RGB_LED_HIGH PAout(8)=1 #define RGB_LED_LOW PAout(8)=0 void RGB_LED_Init(void); void RGB_LED_Write0(void); void RGB_LED_Write1(void); void RGB_LED_Reset(void); void RGB_LED_Write_Byte(uint8_t byte); void RGB_LED_Write_24Bits(uint8_t red, uint8_t green, uint8_t blue); void RGB_LED_Show(uint8_t dat[]); #endif ```c #include “WS2812.h” #include “delay.h” void RGB_LED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_SetBits(GPIOA, GPIO_Pin_8); } void RGB_LED_Write0(void)//WS2812写0码 { RGB_LED_HIGH; __nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop(); __nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop(); __nop();__nop(); RGB_LED_LOW; __nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop(); __nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop(); __nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop(); } void RGB_LED_Write1(void)//WS2812写1码 { RGB_LED_HIGH; __nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop(); __nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop(); __nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop(); __nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop(); __nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop(); __nop();__nop();__nop();__nop();__nop();__nop();__nop(); RGB_LED_LOW; __nop();__nop(); } void RGB_LED_Reset(void)//WS2812复位操作 { RGB_LED_LOW; delay_us(80); } void RGB_LED_Write_Byte(uint8_t byte)//WS2812写1字节 { uint8_t i; for(i=0;i《8;i++) { if(byte&0x80) { RGB_LED_Write1(); } else { RGB_LED_Write0(); } byte 《《= 1; } } void RGB_LED_Write_24Bits(uint8_t red, uint8_t green, uint8_t blue)//WS2812写1个像素点 { RGB_LED_Write_Byte(green); RGB_LED_Write_Byte(red); RGB_LED_Write_Byte(blue); } void RGB_LED_Show(uint8_t dat[])//WS2812写多个个像素点 { int16_t i; RGB_LED_Reset(); for(i=0; i《LED_Nums; i++) { RGB_LED_Write_24Bits(dat[3*i], dat[3*i+1], dat[3*i+2]); } RGB_LED_HIGH; } 在源文件中,为了提高STM32的IO翻转速度,采用了位带输出,然后再使用_nop();来调整延时,使其满足WS2812驱动信号的要求。有条件的可以使用逻辑分析仪或示波器来调整延时。这个笔者当时调整了有一会时间,需要点耐心。 本次的电脑屏幕氛围灯,单片机采用串口与PC通信。 接下来是串口接收程序。 void USART1_IRQHandler(void)//串口1中断服务程序 { static uint8_t k=0,i=0,rebuf[3*LED_Nums+2]={0}; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { rebuf[k++] = USART_ReceiveData(USART1);//读取接收到的数据 if(!(rebuf[0]==0xaa))//如果帧头错误,清缓存 { k = 0; rebuf[0]=0; printf(“err!!!”); } if(k == 3*LED_Nums + 2)//数据接收完毕 { if(rebuf[3*LED_Nums + 1] == 0x55)//判断帧尾 { for(i = 1; i《3*LED_Nums + 1; i++) { LED_data[i-1] = rebuf[i]; show_flag = 1; } } k=0;//清缓存 } } } 串口程序,是随便找个串口中断接收的例程修改的,只需修改串口中断服务函数即可。可以看到,本次笔者自定义了一个简单的通信协议,其为0xaa,R,G,B,R,G,B…,0x55。 主程序也很简单,如下。 while(1) { if(show_flag==1) { RGB_LED_Show(LED_data); show_flag=0; printf(“show ok!”); } } 自此,下位机的程序编写完毕。接下来,我们只需要让电脑通过串口向单片机发送相应像素点的颜色值即可。 上位机 本次上位机采用C#开发,这次也算我自学C#编程的一个练习项目。笔者主要是想学成后,可以自己做电脑与单片机的基础通信,其他东西都是需要用时,参考大佬的程序,一点一点修改成自己想要的样子。 上位机主要是串口通信部分和屏幕颜色获取部分。 颜色获取程序如下。 int len = 60; //设置像素点 Byte lightness = Convert.ToByte(textBox1.Text); //获取亮度设置 int x = 0, y = 200; //截图起始点 Bitmap RGB_Led = new Bitmap(1920, 1); Graphics g = Graphics.FromImage(RGB_Led); Byte[] fram_data = new Byte[3]; Byte[] GRB_data_last = new Byte[3 * len]; Byte[] GRB_data_curr = new Byte[3 * len]; Byte RGB_r = 0, RGB_g = 0, RGB_b = 0; fram_data[0] = 0xAA; fram_data[1] = lightness; fram_data[2] = 0x55; //g.CopyFromScreen(new Point(x, y), new Point(0, 0), new Size(1, 120)); g.CopyFromScreen(x, y, 0, 0, new Size(1920, 1));//起始坐标想x,y,取一个1920*1的屏幕图像 for (int i = 0; i 《 len; i++) { Color color_start = RGB_Led.GetPixel(16 * 2 * i, 0); //隔每16点取颜色 RGB_r = color_start.R; RGB_g = color_start.G; RGB_b = color_start.B; GRB_data_last[3 * i] = RGB_r; GRB_data_last[3 * i + 1] = RGB_g; GRB_data_last[3 * i + 2] = RGB_b; } serialPort1.Write(fram_data, 0, 1); //serialPort1.Write(fram_data, 1, 1); serialPort1.Write(GRB_data_curr, 0, 3 * len); serialPort1.Write(fram_data, 2, 1); 程序的思路很简单,就是定时获取屏幕固定像素点的颜色的RGB分量,然后按之前设计通信协议发送给单片机。具体的也不细讲了,上位机还有很多没有完善的地方,比如现在实现获取一个像素点,更好的是获取一块区域的像素点的颜色平均值,另外为了灯条显示效果更加顺滑,对获取到颜色数据也需要一些算法来处理。 不过,网上专门的取色软件Prismatik。它是我做完这个练习项目后才看到的。我也下载该软件测试过,发现其有一点不稳定,串口数据经常中断。不过,看其他人配合Arduino做出来效果还不错,不知道是我哪里出现了问题。 上位机运行结果如下。 硬件 硬件部分,我打了一块小驱动板,顺便也是为了验证自己的一些想法,方便以后设计电路的时候参考。 一是,USB转串口芯片采用CH340E,它不需要外部晶振,体积也很小。 二是,串口和SWD下载接口共用Type-C接口,通过拨码开关进行选择。 总结一下,最后的效果并不是很理想,程序算法还需要优化,有机会我也再尝试Prismatik软件,因为它的效果确实可人。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1641 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1562 浏览 1 评论
988 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
691 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1608 浏览 2 评论
1869浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
655浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
525浏览 3评论
540浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
512浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-26 02:07 , Processed in 0.675817 second(s), Total 75, Slave 56 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号