导航

    全志在线开发者论坛

    • 注册
    • 登录
    • 搜索
    • 版块
    • 话题
    • 在线文档
    • 社区主页

    【R128】应用开发案例——驱动 WS2812 流水灯

    A Series
    r128开发指南
    1
    1
    1125
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • livpo
      livpo LV 6 最后由 q1215200171 编辑

      基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。

      • DshanMCU-R128s2-R16N16模组:39.9元
      • DshanMCU-R128s2-DEVKIT开发板:59.9元

      R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail

      “主图_01”为智能对象-1.jpg

      驱动 WS2812 流水灯

      本文案例代码 下载地址
      驱动 WS2812 流水灯案例代码 https://www.aw-ol.com/downloads?cat=24

      R128-DevKit 拥有4颗 WS2812 LED,本文将详细叙述如何点亮他们。

      image-20230730203429678.png

      LEDC 模块简介

      image-20230730203939070.png

      LEDC 硬件方框图如上图所示,CPU 通过 APB 总线操作 LEDC 寄存器来控制 LEDC;当 CPU配置好 LEDC 的相关寄存器之后,通过 CPU 或 DMA 将 R、G、B 数据从 DRAM 搬到 LEDC FIFO 中,启动 LEDC 之后就可以通过 PIN 脚向外部的 LED 发送数据了。

      image-20230324173621026.png

      LED 典型电路如图所示,其中 DI 表示控制数据输入脚,DO 表示控制数据输出脚。DI 端接收从控制器传过来的数据,每个 LED 内部的数据锁存器会存储 24bit 数据,剩余的数据经过内部整形处理电路整形放大后通过 DO 端口开始转发输出给下一个级联的 LED。因此,每经过一个LED,数据减少 24bit。

      image-20230324173635697.png

      注意,如果在单次直接设置第 n 个 LED 的亮度和色彩的时候,前面 n-1 个 LED 的亮度数据会在第 n 个 LED 的数据前发送,不过这些数据将会是原来 n-1 个 LED 的亮度数据。

      由于拥有独立的 LEDC 模块,在 R128 平台上驱动 WS2812 类似的 RGB LED 不需要使用 SPI 模拟,也不需要使用 PWM 配置时序。直接使用这个模块即可。

      设置 LEDC 驱动

      运行 mrtos_menuconfig 进入配置页面。前往下列地址找到 LEDC Devices

      Drivers Options  --->
          soc related device drivers  --->
                  LEDC devices --->
                      [*] enable ledc driver
      

      找到 LEDC Devices

      image-20230730203555869.png

      勾选如下选项

      image-20230730204157518.png

      配置 LEDC 参数

      参考电路图可知,LEDC 模块连接的是 R128 的 PA13 引脚。参考手册可知 MUX 为 7

      image-20230730204335088.png

      前往 lichee/rtos/drivers/rtos-hal/hal/source/ledc/platform/ledc_sun20iw2.h 并编辑 LEDC 的引脚和MUX

      #define LEDC_PIN	GPIOA(13)
      #define LEDC_PINMUXSEL	7
      

      image-20230730204505356.png

      然后编辑 lichee/rtos/drivers/rtos-hal/hal/source/ledc/hal_ledc.c 配置 WS2812 的时序参数:

      struct ledc_config ledc_config = {
      	.led_count = 4,
      	.reset_ns = 84,
      	.t1h_ns = 1000,
      	.t1l_ns = 1000,
      	.t0h_ns = 580,
      	.t0l_ns = 1000,
      	.wait_time0_ns = 84,
      	.wait_time1_ns = 84,
      	.wait_data_time_ns = 600000,
      	.output_mode = "GRB",
      };
      

      image-20230730204634317.png

      编译测试

      编译后烧录开发板

      image-20230730205555943.png

      可以用命令 hal_ledc 来测试

      hal_ledc <LED号> <R|G|B> <亮度>
      

      点亮红色 LED

      运行命令

      hal_ledc 1 R 100
      

      image-20230730205753242.png

      即可点亮第一颗 LED

      image-20230730205731463.png

      点亮绿色 LED

      运行命令

      hal_ledc 2 G 100
      

      image-20230730205921963.png

      第二颗 LED 即可点亮绿色

      image-20230730205956129.png

      实现七彩流水灯

      前往项目文件夹编辑 main.c,这里我选择在 M33 核心上编写程序,所以选用的是 lichee/rtos/projects/r128s2/module_m33/src/main.c ,如果是编写 C906 核心的程序,请修改 lichee/rtos/projects/r128s2/module_c906/src/main.c

      #include <sunxi_hal_ledc.h>
      #include <hal_cmd.h>
      #include <hal_timer.h>
      
      // 使用RGB 分量合成颜色值
      #define MERAGECOLOR(G, R, B) (((uint32_t)G << 16) | ((uint16_t)R << 8) | B)
      #define PIXEL_NUM 4
      
      // 生成颜色
      uint32_t WS281x_Wheel(uint8_t wheelPos) {
        wheelPos = 255 - wheelPos;
        if (wheelPos < 85) {
          return MERAGECOLOR(255 - wheelPos * 3, 0, wheelPos * 3);
        }
        if (wheelPos < 170) {
          wheelPos -= 85;
          return MERAGECOLOR(0, wheelPos * 3, 255 - wheelPos * 3);
        }
        wheelPos -= 170;
        return MERAGECOLOR(wheelPos * 3, 255 - wheelPos * 3, 0);
      }
      
      // 亮度设置
      uint32_t WS281xLSet(uint32_t rgb, float k) {
      	uint8_t r, g, b;
      	float h, s, v;
      	uint8_t cmax, cmin, cdes;
       
      	r = (uint8_t) (rgb >> 16);
      	g = (uint8_t) (rgb >> 8);
      	b = (uint8_t) (rgb);
       
      	cmax = r > g ? r : g;
      	if (b > cmax)
      		cmax = b;
      	cmin = r < g ? r : g;
      	if (b < cmin)
      		cmin = b;
      	cdes = cmax - cmin;
       
      	v = cmax / 255.0f;
      	s = cmax == 0 ? 0 : cdes / (float) cmax;
      	h = 0;
       
      	if (cmax == r && g >= b)
      		h = ((g - b) * 60.0f / cdes) + 0;
      	else if (cmax == r && g < b)
      		h = ((g - b) * 60.0f / cdes) + 360;
      	else if (cmax == g)
      		h = ((b - r) * 60.0f / cdes) + 120;
      	else
      		h = ((r - g) * 60.0f / cdes) + 240;
       
      	v *= k;
       
      	float f, p, q, t;
      	float rf, gf, bf;
      	int i = ((int) (h / 60) % 6);
      	f = (h / 60) - i;
      	p = v * (1 - s);
      	q = v * (1 - f * s);
      	t = v * (1 - (1 - f) * s);
      	switch (i) {
      	case 0:
      		rf = v;
      		gf = t;
      		bf = p;
      		break;
      	case 1:
      		rf = q;
      		gf = v;
      		bf = p;
      		break;
      	case 2:
      		rf = p;
      		gf = v;
      		bf = t;
      		break;
      	case 3:
      		rf = p;
      		gf = q;
      		bf = v;
      		break;
      	case 4:
      		rf = t;
      		gf = p;
      		bf = v;
      		break;
      	case 5:
      		rf = v;
      		gf = p;
      		bf = q;
      		break;
      	default:
      		break;
      	}
       
      	r = (uint8_t) (rf * 255.0);
      	g = (uint8_t) (gf * 255.0);
      	b = (uint8_t) (bf * 255.0);
       
      	return ((uint32_t) r << 16) | ((uint32_t) g << 8) | b;
      }
      
      // 延时函数
      static inline int msleep(int ms) {
          vTaskDelay(ms / portTICK_RATE_MS); 
      }
      
      // 测试 LEDC
      int ledc_test_loop() {
        int i = 0, j = 0, err;
        int mode = 0;
        uint8_t R = 0, G = 0, B = 0;
      
        err = hal_ledc_init();
        if (err) {
          printf("ledc init error\n");
          return -1;
        }
      
        while (1) {
          for (j = 0; j < 256; j++) {
            for (i = 0; i < PIXEL_NUM; i++) {
              sunxi_set_led_brightness(
                  i + 1, WS281xLSet(WS281x_Wheel(((i * 256 / PIXEL_NUM) + j) & 255), 0.2));
              msleep(1);
            }
            msleep(10);
          }
        }
        return 1;
      }
      

      并且将测试函数加入到 cpu0_app_entry 中。

      image-20230730210755413.png

      重新烧录即可实现七彩流水灯

      image-20230730210852640.png

      1 条回复 最后回复 回复 引用 分享 0
      • 1 / 1
      • First post
        Last post

      Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

      行为准则 | 用户协议 | 隐私权政策