当前位置: 首页> 游戏> 攻略 > 中国电商平台排行榜前十_网站源码怎么打开_常见的网络营销平台有哪些_企业关键词优化最新报价

中国电商平台排行榜前十_网站源码怎么打开_常见的网络营销平台有哪些_企业关键词优化最新报价

时间:2025/7/10 14:00:18来源:https://blog.csdn.net/CATTLE_L/article/details/144339104 浏览次数:0次
中国电商平台排行榜前十_网站源码怎么打开_常见的网络营销平台有哪些_企业关键词优化最新报价

文章目录

  • 1、前言
  • 2、结果展示
  • 3、接线
  • 4、SPI驱动WS2812原理
    • 4.1、0码要发送的字节
    • 4.2、1码要发送的字节
    • 4.3、SPI时钟频率
  • 5、点亮RGB
    • 5.1、亮绿灯
    • 5.2、亮红灯
    • 5.3、亮蓝灯
    • 5.4、完整程序
  • 6、RGB呼吸灯
  • 7、总结

1、前言

事情是这样的,前段时间,写了一个基于RK356x/RK3588的WS2812驱动,实验发现,单独点亮RGB灯倒是没什么问题。但点灯只是第一步,因为后面还要做呼吸灯,所以又经过实验发现,在基于此驱动实现的呼吸灯应用效果差强人意,过程中会频繁出现灯灭和颜色误识别的情况。为什么呢?在之前的驱动里,是利用了程序执行的延时来完成码0和码1的传输,加上呼吸灯需要对WS2812频繁操作,所以容易出现不稳定的情况。

失败的效果如下(四个颜色呼吸渐变):
在这里插入图片描述

2、结果展示

再看SPI方式驱动的WS2812呼吸灯效果(四个颜色呼吸渐变):
在这里插入图片描述

3、接线

SPI控制器的DO接到WS2812的IN

4、SPI驱动WS2812原理

需要确定三个参数:0码要发送的字节、1码要发送的字节、SPI时钟频率

4.1、0码要发送的字节

0码的高电平(T0H)时间需要控制在220ns ~ 380ns,低电平(T0L)时间需要控制在580ns ~ 1.6us。

这里T0H取300ns,T0L取700ns,加起来刚好凑整1us的周期,T0H占30%,T0L占70%。

现在利用各自的占比来确认要发送的8个bit:

T0H所占的bit:(30% / 100%) * 8 = 2.4bit≈2bit

T0L所占的bit:(70% / 100%) * 8 = 5.6bit≈6bit

所以0码使用11000000(0xC0)表示。最后SPI发送0xC0就会先拉高300ns,拉低700ns(这是设想,要确定SPI时钟频率后才能真正实现)。

4.2、1码要发送的字节

1码的高电平(T1H)时间需要控制在580ns ~ 1.6us,低电平(T1L)时间需要控制在220ns ~ 420ns。

这里T1H取700ns,T1L取300ns,加起来刚好凑整1us的周期,T1H占70%,T1L占30%。

现在利用各自的占比来确认要发送的8个bit:

T1H所占的bit:(70% / 100%) * 8 = 5.6bit≈6bit

T1L所占的bit:(30% / 100%) * 8 = 2.4bit≈2bit

所以1码使用11111100(0xFC)表示。最后SPI发送0xFC就会先拉高700ns,拉低300ns(这是设想,要确定SPI时钟频率后才能真正实现)。

4.3、SPI时钟频率

频率的确认是最关键的,这决定了是否能正确发送0码和1码。

上面讲到发送一个字节需要1us的周期,所以SPI传输每bit的时间为:1000/8=125ns,换成频率即是1/125=8Mhz,所以SPI时钟频率需要设置为8Mhz。

5、点亮RGB

SPI发送函数的实现如下:

/* spi.c */... /*****************************
* @brief : 向 SPI 总线写入n个字节数据
* @param : send_buf - 待写入的数据
* @param : send_buf_len - 待写入的数据长度
* @return: 无返回值
* @note  : 通过 SPI 总线发送n个字节的数据。
*****************************/
void spi_write_nbyte_data(unsigned char *send_buf, unsigned int send_buf_len)
{struct spi_ioc_transfer	xfer[2];unsigned char recv_buf[send_buf_len];int status;if(send_buf == NULL || send_buf_len < 1)return;memset(xfer, 0, sizeof(xfer));memset(recv_buf, 0, sizeof(send_buf_len));xfer[0].tx_buf = (unsigned long)send_buf;xfer[0].rx_buf = (unsigned long)recv_buf;xfer[0].len = send_buf_len;status = ioctl(fd_spidev, SPI_IOC_MESSAGE(1), xfer);if (status < 0) {perror("SPI_IOC_MESSAGE");return;}
}...

5.1、亮绿灯

我这个灯是按照GRB的顺序发送数据。

/* main.c */...unsigned char send_buf[24] = {0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc};
spi_write_nbyte_data(send_buf, sizeof(send_buf));...

5.2、亮红灯

/* main.c */...unsigned char send_buf[24] = {0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc};
spi_write_nbyte_data(send_buf, sizeof(send_buf));...

5.3、亮蓝灯

/* main.c */...unsigned char send_buf[24] = {0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc};
spi_write_nbyte_data(send_buf, sizeof(send_buf));...

5.4、完整程序

spi.h

/*
*
*   file: spi.h
*   updata: 2024-12-05
*
*/#ifndef _SPI_H
#define _SPI_H#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <gpiod.h>
#include <stdint.h>
#include <linux/spi/spidev.h>
#include <pthread.h>typedef struct spi_operations
{void (*spi_write_then_read)(unsigned char *send_buf, unsigned int send_buf_len, unsigned char *recv_buf, unsigned int recv_buf_len);void (*spi_write_byte_data)(unsigned char data);void (*spi_write_nbyte_data)(unsigned char *send_buf, unsigned int send_buf_len);pthread_mutex_t *mutex;
}spi_operations_t;typedef enum
{SPIMODE0 = SPI_MODE_0,SPIMODE1 = SPI_MODE_1,SPIMODE2 = SPI_MODE_2,SPIMODE3 = SPI_MODE_3,
}SPI_MODE;typedef enum
{S_1M    = 1000000,S_6_75M = 6750000,S_8M    = 8000000,S_13_5M = 13500000,S_27M   = 27000000,
}SPI_SPEED;int spi_init(const char *spi_dev);
void spi_exit();
spi_operations_t *get_spi_ops();#endif

spi.c

/*
*
*   file: spi.c
*   updata: 2024-12-05
*
*/#include "spi.h"static int fd_spidev;
static int init_flag = 0;       // 1已初始化 0未初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/****************************** @brief : 初始化 SPI 设备* @param : spi_dev - SPI 设备路径* @return: 成功返回 0,失败返回 -1* @note  : 初始化 SPI 接口,配置 SPI 参数。*****************************/
int spi_init(const char *spi_dev)
{int ret; SPI_MODE mode;char spi_bits;SPI_SPEED spi_speed;fd_spidev = open(spi_dev, O_RDWR);if (fd_spidev < 0) {printf("open %s err\n", spi_dev);return -1;}/* mode */mode = SPIMODE0;ret = ioctl(fd_spidev, SPI_IOC_WR_MODE, &mode);                //mode 0if (ret < 0) {printf("SPI_IOC_WR_MODE err\n");return -1;}/* bits per word */spi_bits = 8;ret = ioctl(fd_spidev, SPI_IOC_WR_BITS_PER_WORD, &spi_bits);   //8bits if (ret < 0) {printf("SPI_IOC_WR_BITS_PER_WORD err\n");return -1;}/* speed */spi_speed = (uint32_t)S_8M;ret = ioctl(fd_spidev, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed);    //1MHz    if (ret < 0) {printf("SPI_IOC_WR_MAX_SPEED_HZ err\n");return -1;}init_flag = 1;return 0;
}/****************************** @brief : 向 SPI 总线写入数据并读取数据* @param : send_buf     - 发送数据的缓冲区*          send_buf_len - 发送数据的长度*          recv_buf     - 接收数据的缓冲区*          recv_buf_len - 接收数据的长度* @return: 无返回值* @note  : 通过 SPI 总线发送和接收数据,发送的数据通过 `send_buf`,接收到的数据存放在 `recv_buf` 中。*****************************/
static void spi_write_then_read(unsigned char *send_buf, unsigned int send_buf_len, unsigned char *recv_buf, unsigned int recv_buf_len)
{struct spi_ioc_transfer	xfer[2];int status;if(init_flag == 0){perror("spidev can not init!\n");return;}if(send_buf == NULL || recv_buf == NULL)return;if(send_buf_len < 1 || recv_buf_len < 1)return;memset(xfer, 0, sizeof(xfer));xfer[0].tx_buf = (unsigned long)send_buf;xfer[0].len = send_buf_len;xfer[1].rx_buf = (unsigned long)recv_buf;xfer[1].len = recv_buf_len;status = ioctl(fd_spidev, SPI_IOC_MESSAGE(2), xfer);if (status < 0) {perror("SPI_IOC_MESSAGE");return;}
}/****************************** @brief : 向 SPI 总线写入一个字节数据* @param : data - 待写入的数据字节* @return: 无返回值* @note  : 通过 SPI 总线发送一个字节的数据。*****************************/
static void spi_write_byte_data(unsigned char data)
{unsigned char buff[1] = {data};if(init_flag == 0){perror("spidev can not init!\n");return;}write(fd_spidev, &buff, 1);
}/****************************** @brief : 向 SPI 总线写入n个字节数据* @param : send_buf - 待写入的数据* @param : send_buf_len - 待写入的数据长度* @return: 无返回值* @note  : 通过 SPI 总线发送n个字节的数据。*****************************/
static void spi_write_nbyte_data(unsigned char *send_buf, unsigned int send_buf_len)
{struct spi_ioc_transfer	xfer[2];unsigned char recv_buf[send_buf_len];int status;if(init_flag == 0){perror("spidev can not init!\n");return;}if(send_buf == NULL || send_buf_len < 1)return;memset(xfer, 0, sizeof(xfer));memset(recv_buf, 0, sizeof(send_buf_len));xfer[0].tx_buf = (unsigned long)send_buf;xfer[0].rx_buf = (unsigned long)recv_buf;xfer[0].len = send_buf_len;status = ioctl(fd_spidev, SPI_IOC_MESSAGE(1), xfer);if (status < 0) {perror("SPI_IOC_MESSAGE");return;}
}/****************************** @brief : 关闭 SPI * @param : none* @return: 无返回值*****************************/
void spi_exit()
{if(fd_spidev >= 0)close(fd_spidev);init_flag = 0;
}static spi_operations_t spi_ops = {.spi_write_then_read = spi_write_then_read,.spi_write_byte_data = spi_write_byte_data,.spi_write_nbyte_data = spi_write_nbyte_data,.mutex = &mutex,
};/****************************** @brief : 获取SPI操作函数* @param : none* @return: 返回spi_operations_t结构体指针*****************************/
spi_operations_t *get_spi_ops()
{return &spi_ops;
};

main.c

/*
*
*   file: main.c
*   update: 2024-12-05
*   usage: 
*       sudo gcc -o main main.c
*       sudo ./main FF0000
*
*/#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <signal.h> 
#include <pthread.h>#include "spi.h"unsigned char send_buf[24];
void update_sendbuff(unsigned char r, unsigned char g, unsigned char b)
{int i = 0;// update gfor (i = 0; i < 8; i++) {send_buf[i] = (g & 0x80) ? (0xFC) : (0xC0);		g <<= 1;		}// update rfor (i = 8; i < 16; i++) {send_buf[i] = (r & 0x80) ? (0xFC) : (0xC0);	r <<= 1;			}// update bfor (i = 16; i < 24; i++) {send_buf[i] = (b & 0x80) ? (0xFC) : (0xC0);	b <<= 1;			}
}int main(int argc, char **argv) 
{int ret;unsigned char r, g, b;spi_operations_t *spi_ops;      // spi操作函数// 初始化spiret = spi_init("/dev/spidev3.0");if(ret < 0)return -1;spi_ops = get_spi_ops();// 参数数量检查if(argc != 2) {printf("Usage: %s <hex_color>\n", argv[0]);printf("e.g. : %s FF0000\n", argv[0]);return -1;}/* 参数1检查 */if(strlen(argv[1]) != 6){printf("Error: The first argument has illegal length.\n");printf("e.g. : %s FF0000\n", argv[0]);return -1;}if (sscanf(argv[1], "%2hhx%2hhx%2hhx", &r, &g, &b) != 3) {  printf("Error: Invalid hex color format.\n");  return -1;  }// 更新颜色数据update_sendbuff(r, g, b);spi_ops->spi_write_nbyte_data(send_buf, sizeof(send_buf));     return 0;
}

6、RGB呼吸灯

RGB三色灯可以通过控制红、绿、蓝三个颜色的分量实现全真色彩显示,同时可实现256级亮度显示。

如熄灭灯是0x000000,点亮蓝灯是0x0000ff,所以可以通过控制低16位来调节蓝灯的亮度,值越小亮度越小。

如发送0x000011时,蓝灯的亮度如下:

发送0x000055时,蓝灯的亮度如下:

发送0x0000ff时,蓝灯的亮度如下:

红灯则是调节中间16位、绿灯是调节高16位。

7、总结

参考文章:SPI驱动ws2812详细解说

关键字:中国电商平台排行榜前十_网站源码怎么打开_常见的网络营销平台有哪些_企业关键词优化最新报价

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: