【GD32F427开发板试用】点亮WS2812B炫彩灯环

news2024/11/23 16:50:31

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动,更多开发板试用活动请关注极术社区网站。作者:HonestQiao

我有一个WS2812B炫彩灯环,搭配精选的背景,非常出镜:

在玩过的板子上,我都要把它点亮。

关于WS2812B的介绍资料,网上有很多,这里就不细说了,只说重点:

  1. WS2812B是单总线控制的:

控制一颗WS2812B与控制多颗WS2812B,方式是一样的,不同的只是每批传送的数据的多少。
我上面的这个灯环,就是24颗串联在一起,第一颗的DIN负责接收控信号,然后每一颗的DOUT,接下一颗的DIN,将控制信号传递过去,直到最后一颗。这种方式,可以连接上千颗一起控制。
需要注意的是,最后一颗的DOUT是留空的。所以上面这个炫彩灯环,首尾是不相连的。

  1. WS2812B控制一颗灯珠,需要24bits的数据,代表着GRB三种颜色值:

控制多颗,则使用多组连续的24bits数据:

每颗WS2812B截取数据中最开始的24bits,然后把剩下的传递给后来者,直到数据发送完毕。

  1. 控制设备,不能直接发送这些bit位的信号,而是要按照一定的规则发送信号,才被检测为对应的bit位:

WS2812B规定了三种信号:0码、1码、reset码,这三种码,通过信号线上的高低电平的特定保持时间来做区分,具体如下:

也就是:

  • 1个码的信号时间长度位1.25us
  • 如果高电平保持0.4us,低电平保持0.85us,则判定为T0码
  • 如果高电平保持0.8us,低电平保持0.45us,则判定为T1码
  • 如果低电平持续至少50us,则判定位RESET码;例如传递第二批控制信号时,需要使用该码

上述数据,如图表所示,都有一定的容错范围,经过以往的测验经验,最终使用如下的值:

  • T0:高0.25us,低1.00us
  • T1:高1.00us,低0.25us
  • RESET:低50us

要实现0.25us的时间控制精度,那么换算成频率位:1/(0.25/1000000) = 4M。也就是控制设备,需要以4Mbits/s这么快的速度,来控制信号的变化,才能满足WS2812B控制信号的要求。

要满足上述的要求,可以有很多种方法。常用的有GPIO翻转,使用SPI的MOSI发送数据等。
有人可能有疑问,UART、I2C发送数据,也能带来信号线的高低电平变化,哪能使用吗?
UART的传送速度,通常波特率最高位1.5Mbits/s;I2C通信,超高速的能达到5M,但一般都达不到,通常可能为400kbits/s,快的位3.4Mbits/s。
而SPI,通常速度都能达到50Mbits/s。
但使用GPIO翻转来控制信号,并非所有的的设备都能达到,有的翻转的速率没有这么快。
经过实测,咱们的GD32F427开发板完全可以满足。所以,这篇分享,就直接使用GPIO翻转这种简单的方式来做了。
在实际使用中,有很多大佬,也研究了各种各样优化的方法,大家感兴趣的话,可以查找资料了解一下。

通过查看原理图和数据手册,可以使用PC1来进行控制,该引脚一般情况下没有直接复用:

具体接线如下:

因为我只打算一次点亮一颗灯珠,所以供电部分,直接使用板载的3.3V、GND即可。
如果需要点亮多颗灯珠,那么应该使用外部电源供电。点亮一颗灯珠的一种颜色,需要20mA电流,三种颜色都点亮,则需要60mA电流,24颗全部点亮,则需要1440mA电流。一般开发板的GPIO,是供不起的全部点亮的。

在前一篇文章Systick系统定时器的使用种,探讨了SysTick的基础使用,可以达到us级别的精确控制。不过亚us级别的控制,就会有一些吃力了。
在MCU的精确时间控制中,还有一种使用nop指令来进行控制的。所谓nop,就是一条空指令,一个最小的机器周期。通过一定数量的nop,从而实现亚us级别的控时。

GD32F427开发板的运行频率高低200MHz,经过实测,每200个nop,刚好经过1us,也就是每个nop为0.005us。那么要达到0.25us的控时,就需要50个nop。

在system_gd32f4xx.c中,有系统时钟的定义:

在 core_cmInstr.h 中,有关于nop的符号定义:

那么我们要定义0.25us,可以参考使用如下的方式:

#define 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的方式,更简洁明了。

前面要求的两个时间分别为0.25us,1.00us,就分别为1个NOP和4个NOP。

经过以上的准备工作,就可以编写实际的控制代码了,具体代码如下:

/*!
    \file    main.c
    \brief   GPIO running WS2812B demo

    \version 2022-11-24, V1.0.0, demo for GD32F4xx
*/


#include "gd32f4xx.h"
#include "gd32f427v_start.h"
#include "systick.h"
#include <stdio.h>

#define 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();    \
    }
    
void GPIO_Init(void)
{
    rcu_periph_clock_enable(RCU_GPIOC);                                                //使能外部时钟

    gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_1);                //配置端口模式
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);    //输出选项配置
    gpio_bit_reset(GPIOC, GPIO_PIN_1);                                                //PC1复位

}

#define LOW 0
#define HIGH 1
#define DIN PC1
#define NUM 24

//拉低DIN保持50us以上
void ws2812_init() {
  unsigned char i;
  gpio_bit_reset(GPIOC, GPIO_PIN_1);
  for (i = 0; i <= 200; i++) {
    NOP;
  }
}

//高电平0.25us,低电平1us
void ws2812_write_0() {
  gpio_bit_set(GPIOC, GPIO_PIN_1);
  NOP;
  gpio_bit_reset(GPIOC, GPIO_PIN_1);
  NOP;
  NOP;
  NOP;
  NOP;
}

//高电平1us,低电0.25us
void ws2812_write_1() {
  gpio_bit_set(GPIOC, GPIO_PIN_1);
  NOP;
  NOP;
  NOP;
  NOP;
  gpio_bit_reset(GPIOC, GPIO_PIN_1);
  NOP;
}


// 写入24bits
void ws2812_write_24bits(unsigned long dat) {
  unsigned char t[NUM] = {0};
  unsigned char i;

  for (i = 0; i < NUM; i++) {
    if (dat >> i & 1) {
      t[i] = HIGH;
    } else {
      t[i] = LOW;
    }
  }

  for (i = 0; i < NUM; i++) {
    if (t[i]) {
      ws2812_write_1();
    } else {
      ws2812_write_0();
    }
  }
}


void ws2812_test() {
  unsigned int i = 0;
  unsigned int j = 0;
  unsigned int index = 1;
  unsigned long colors[8] = {0x000000, 0xff0000, 0x00ff00, 0x0000ff,
                             0xffff00, 0xff00ff, 0x00ffff, 0xffffff};

  ws2812_init();

  while (1) {
    for (i = 0; i < NUM; i++) {
      if (index % NUM == i) {
        ws2812_write_24bits(colors[index % 7 + 1]);
      } else {
        ws2812_write_24bits(colors[0]);
      }
    }
    index++;
    if (index >= NUM) {
      index = 0;
    }
    // 延时0.1秒
    for (i = 0; i <= 200*50*100; i++) {
      NOP;
    }
  }
}

int main(void)
{
    systick_config();    //配置系统时钟
    GPIO_Init();
    
    ws2812_test();
}

上述代码中,关键调用说明如下:

  • gpio_bit_set(GPIOC, GPIO_PIN_1):设置高电平
  • gpio_bit_reset(GPIOC, GPIO_PIN_1):重置为低电平
  • ws2812_init():重置WS2812B控制,拉低,保持50us
  • ws2812_write_0():输出T0码,高0.25us,低0.25us
  • ws2812_write_1():输出T1码,高1.00us,低0.25us
  • ws2812_write_24bits():批量生成和输出T0、T1
  • ws2812_test():使用index自增,表示当前需要点亮的灯珠,一次发送24组数据,每组24bits,且使用colors预定义8种颜色,colors[0]实际上表示熄灭。

编译以上代码,然后下载到开发板中,就可以看到炫彩的灯光,在灯环上欢快的跳跃了:

动图封面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/168679.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法之美~堆

如何理解“堆”堆是一种特殊的树&#xff0c;只要满足如下两点&#xff1a;堆是一个完全二叉树&#xff1b;堆中每个节点的值都必须>&#xff08;或<&#xff09;其子树中每个节点的值。大顶堆&#xff1a;每个节点的值都>子树中每个节点的值&#xff1b;小顶堆&#…

完美的Pornhub风格的Logo生成器,在线工具

pornhub是全球最大的Sex影片分享网站之一。于2007年成立于加拿大蒙特利尔&#xff0c;是属于大型的色情视频分享类网站&#xff0c;被视为是“Sex2.0”的先驱&#xff0c;在Alexa上排名第80位&#xff08;最高时曾跻身前30&#xff09;。 同样这个网站的logo风格也别具一格&am…

C 程序设计教程(17)—— 循环结构程序设计练习题

C 程序设计教程&#xff08;17&#xff09;—— 循环结构程序设计练习题 该专栏主要介绍 C 语言的基本语法&#xff0c;作为《程序设计语言》课程的课件与参考资料&#xff0c;用于《程序设计语言》课程的教学&#xff0c;供入门级用户阅读。 目录C 程序设计教程&#xff08;17…

3DMAX砖墙神器WallBuilder:快速生成常见砖墙插件教程

3DMAX一键快速生成砖墙插件&#xff08;3DMAX砖墙神器——快速常见砖墙&#xff09;WallBuilder&#xff0c;用来生成各种砖砌或石砌墙体、地面的插件。 【主要特性】 -偏移以将不同的墙放在一起&#xff1b; -可以按照样条曲线创建墙&#xff1b; -随机化砖块的随机函数&am…

【寒假每日一题】DAY.9 猜名次

目录 一、题目内容 二、思路 思路1 思路2 思路3 三、代码实现 一、题目内容 5位运动员参加了10米台跳水比赛&#xff0c;有人让他们预测比赛结果&#xff1a;A选手说&#xff1a;B第二&#xff0c;我第三&#xff1b;B选手说&#xff1a;我第二&#xff0c;E第四&#xff1b;…

COS插件入驻Discuz!x

Discuz!Discuz! 平台&#xff0c;由一群高擎互联网人在倾情支持&#xff0c; 他们来自于腾讯Discuz! 创业团队成员以及优秀的开发者。在中国互联网风云变迁中&#xff0c;Discuz! 20多年间为300万企业及站长赋能&#xff0c;秉承“开放、连接、共赢”的精神&#xff0c;倡导与生…

ZStack协议栈点对点通信

这里是zstack3.0安装包&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1-N8FFQ86zenF1iq-wgkmJQ?pwd2023 提取码&#xff1a;2023 新建自己的zstack工程这篇写得详细&#xff1a;新建 这个点对点通信主要是终端节点向协调器发送命令D1,协调器收到命令后判断数据是否为…

聚焦技术与体验极致提升,阿里云视频云连续5年领跑!

全球领先的IT市场研究和咨询公司IDC发布 《中国视频云市场跟踪&#xff08;2022上半年&#xff09;》 阿里云连续五年稳居 中国视频云整体市场份额第一 整体市场份额占比达25.4% 近日&#xff0c;全球领先的IT市场研究和咨询公司IDC发布的《中国视频云市场跟踪&#xff08;…

算法训练营 day21 二叉树 二叉搜索树的最小绝对差 二叉搜索树中的众数 二叉树的最近公共祖先

算法训练营 day21 二叉树 二叉搜索树的最小绝对差 二叉搜索树中的众数 二叉树的最近公共祖先 二叉搜索树的最小绝对差 530. 二叉搜索树的最小绝对差 - 力扣&#xff08;LeetCode&#xff09; 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最…

《啊哈算法》第四章之深度优先搜索

✌好听的歌一起分享&#xff01; 稻香 (女声版) - 余不不 - 单曲 - 网易云音乐 目录 模板 例子 1&#xff0c;关于遍历 2&#xff0c;关于边界 正文 1&#xff0c;概念 2&#xff0c;解救小哈 例子源码和题目 1&#xff0c;小学奥数 2&#xff0c;全排列 3&#x…

【自学Docker 】Docker inspect命令

Docker inspect命令 大纲 docker inspect教程 使用 docker inspect 命令可以用来获取 Docker容器 或者 Docker镜像 的元数据。该命令后面的 CONTAINER 可以是容器Id&#xff0c;或者是容器名。 docker inspect语法 haicoder(www.haicoder.net)# docker inspect [OPTIONS] N…

python小知识

一、pip config list -v#pip在哪里寻找pip.conf文件 阿里云镜像&#xff1a; 二、安装anaconda(阿里云镜像库) 1、安装完成&#xff0c;命令行输入&#xff1a;conda config生成.condarc文件&#xff08;运行期配置文件&#xff09; 2、如果原本的源中的源地址是 https&…

webService速通教学(送源码)

什么是WebService webService是一种古老的互联网通讯方法。通过http通讯&#xff0c;将数据封装成XML的形式在网络中传输。习惯Java开发的伙伴肯定会对其嗤之以鼻&#xff0c;为什么不用Json传输&#xff0c;为什么不直接通过url拼参数&#xff1f;最大的两个因素是&#xff1…

Nginx基础03:配置文件nginx.conf(Part2)

上一篇文章概述与罗列了"全局配置块、events配置块、http全局块"的基本配置与属性&#xff0c;本篇文章将继续深入server块的配置项&#xff0c;以及相关应用。上篇文章地址&#xff1a;Nginx基础02&#xff1a;配置文件nginx.conf&#xff08;Part1&#xff09;如何…

从“工业报国为己任”到“科技创造优质生活”——华阳集团三十周年记

2022年10月28日&#xff0c;ADAYO华阳集团(002906.SZ)发布了一份“亮眼”的三季报。2022年1-9月公司实现营业收入40.08亿元&#xff0c;同比增长28.03%&#xff0c;实现归母净利润2.67亿元&#xff0c;同比增长28.28%。其中第三季度营业收入、净利润绝对值均为上市以来单季度最…

Android 深入系统完全讲解(23)

OpenGL 和 OpenGLES 简介 OpenGL(Open Graphics Library)意为开放图形库&#xff0c;是一个跨平台的图形 API&#xff0c;用于指定 3D 图 形处理硬件中的软硬件编程接口。OpenGL 一般用于图形工作站&#xff0c;PC 端使用。由于性能和可 移植性等各方面原因&#xff0c;在移动端…

分享72个PHP源码,总有一款适合您

PHP源码 分享72个PHP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 72个PHP源码下载链接&#xff1a;https://pan.baidu.com/s/11BEwLdHiZF0DqB3Bhb3FaQ?pwdjoxq 提取码&#xff…

使用docker搭建版本控制系统-SVN

作者简介&#xff1a;一名Linux运维工程师&#xff0c;主要分享自己遇到的学习过程与遇到的问题。 座右铭&#xff1a;选择时尽心&#xff0c;行动时尽力&#xff0c;坚持时无怨&#xff0c;结束时无悔。 个人公众号&#xff1a;早九晚十二&#xff0c;欢迎大家留言交流…

day41|● 343. 整数拆分 ● 96.不同的二叉搜索树

题目链接:343. 整数拆分 1.代码 暴力做法&#xff1a;n前进行组合搜索得出二维数组result存放结果&#xff0c;然后对里面的元素进行乘积&#xff0c;谁最大就是结果 2.递归做法 class Solution { public:int integerBreak(int n) {vector<int>f(n 5, 0);f[2] 1;for …

LinkedList链表知识点概括

作者&#xff1a;爱塔居的博客_CSDN博客-JavaSE,数据结构领域博主 专栏&#xff1a;数据结构 作者简介&#xff1a;大三学生&#xff0c;希望2023年迎来更优秀的自己&#xff01;希望跟大家一同进步~ 文章目录 前言 一、链表的基本概念 二、面试题实战 前言 顺序表/ArrayList:…