【龙芯2K500先锋板】点亮RGB多彩发光板

news2024/11/24 23:10:01

文章目录

    • 一、硬件准备
      • 1.1 DFRobot RGB多彩发光板
      • 1.2 龙芯2K0500开发板
    • 二、背景知识
    • 三、实现点灯
      • 3.1 手动点亮
      • 3.2 手动熄灭
      • 3.3 实现PwmController
      • 3.4 实现呼吸灯效果
    • 四、效果演示
    • 五、完整代码
    • 六、参考链接

前段时间乔帮主送了几个DFRobot的RGB多彩发光板,官方的演示视频看起来很绚丽,今天准备用龙芯开发板点亮这个RGB多彩发光板,实现炫彩呼吸灯。

本文接下来将会介绍——如何使用Linux系统在龙芯2K0500先锋板上,输出PWM方波点亮多彩LED发光板。本文介绍的发方法同样可以用于控制LED的亮度、三色LED的亮度、控制舵机旋转角度等其他使用PWM信号驱动的外设。

一、硬件准备

本文所需硬件包括:

  • 龙芯2K500先锋板
  • DFRobot RGB多彩发光板

1.1 DFRobot RGB多彩发光板

多彩发光板长这样:

多彩发光板引脚说明:

内部是三个共阳极的发光二极管,和三色LED很相似。

1.2 龙芯2K0500开发板

龙芯2K0500开发板的IO扩展口上预留了PWM的引脚,具体如下图:

image-20230117194258171

这里使用PWM0、PWM1、PWM2三个引脚连接到多彩发光板,连接关系如下:

  • 10号针(PWM0)连接到 多彩发光板的R脚
  • 7号针(PWM1)连接到 多彩发光板的G脚
  • 8号针(PWM2)连接到 多彩发光板的B脚
  • 24号针(P3V3)连接到 多彩发光板的**+脚**;

二、背景知识

开始之前,先简单介绍一点背景知识。这里主要参考的是Linux内核文档《Pulse Width Modulation (PWM) interface》。

以下是用户层sysfs接口一段文档的翻译(PS:水平有限,如有错误,烦请指正):

用户空间通过sysfs来使用pwm功能,相关文件都暴露在 /sys/class/pwm/ 目录下。主芯片(我们这里的龙芯2K0500)的每个PWM控制器体现为pwmchipN目录(其中N是主芯片的第几个PWM控制器),这个目录里面会有:

  • npwm 表示这个PWM控制器有几个通道(只读的)
  • export 用于将一个PWM通道导出到sysfs(只写的,PS:导出之后才有pwm0之类的子目录,如果导出0的话);
  • unexport 用于将PWM通道的sysfs目录隐藏;

PWM通道的数字是,从0开始到npwm-1;

当一个PWM通道导出后,pwmX目录将会在pwmchipN目录下被创建,X就是导出的PWM通道编号;同时伴随着以下属性可用:

  • period PWM信号的总周期(可读可写)。它的值是纳秒为单位的并且是PWM一整个周期的时间(包括active和inactive的);
  • duty_cycle PWM信号的active状态的时间(可读可写);也是纳秒为单位,并且必须小于period的值;
  • polarity 用于改变PWM信号的极性(可读可写);写这个属性仅在PWM芯片支持修改时才有用(work),polarity只能在PWM还没有使能时修改。值是字符串“normal”或者“inversed”;
  • enable 启用或禁用PWM信号(可读可写);
    • 0 禁用
    • 1 启用

三、实现点灯

有了以上知识,我们就可以在龙芯2K0500开发板上使用PWM点亮多彩发光板了。

3.1 手动点亮

开机后,在shell中使用ls命令查看/sys/class/pwm目录:

# ls /sys/class/pwm/
pwmchip0  pwmchip1  pwmchip2  pwmchip3  pwmchip4  pwmchip5
#

跳转到 /sys/class/pwm/pwmchip0 目录,查看文件:

# cd /sys/class/pwm/pwmchip0/
#
# ls
device  export  npwm  power  subsystem  uevent  unexport
#

向export写入0之后,查看:

# echo 0 > export
#
# ls -al
total 0
drwxr-xr-x 4 root root     0 2022-06-01 10:20 .
drwxr-xr-x 3 root root     0 2022-06-01 10:20 ..
lrwxrwxrwx 1 root root     0 2022-06-01 10:21 device -> ../../../1ff5c000.pwm
--w------- 1 root root 16384 2022-06-01 10:24 export
-r--r--r-- 1 root root 16384 2022-06-01 10:21 npwm
drwxr-xr-x 2 root root     0 2022-06-01 10:20 power
drwxr-xr-x 3 root root     0 2022-06-01 10:24 pwm0
lrwxrwxrwx 1 root root     0 2022-06-01 10:21 subsystem -> ../../../../../../class/pwm
-rw-r--r-- 1 root root 16384 2022-06-01 10:20 uevent
--w------- 1 root root 16384 2022-06-01 10:24 unexport
#

可以看到,多出来一个pwm0目录;

跳转到pwm0目录后,使用echo分别写文件:

# echo 1000000000 > period
#
# echo 500000000 > duty_cycle
#
# echo 1 > enable
#

此时将会看到多彩发光板红色闪烁,频率为每秒一次。

这里写入的period值1000000000,产生的PWM信号周期正好是1秒(纳秒为单位),duty_cycle值500000000是它的一半,方便观察。

3.2 手动熄灭

类似的,使用echo也可以停止PWM信号,从而熄灭多彩发光板;

# echo 0 > enable

类似的,向duty_cycle写入其他值,可以改变PWM信号的占空比:

# echo 100000000 > duty_cycle
# echo 1 > enable

3.3 实现PwmController

有了上面的实验,我们就可以实现用代码控制输出PWM信号了。

class PwmController
{
public:
    PwmController(int chip, int chan = 0) : chip_(chip)
    {
        Export(true);
    }

    ~PwmController()
    {
        Export(false);
    }

    bool Start()
    {
        return Enable(true);
    }

    bool Stop()
    {
        return Enable(false);
    }

    bool SetFreq(int freq)
    {
        if (freq <= 0)
        {
            return false;
        }

        period_ = 1000000000uL / freq;
        return WriteInt(periodPath_, period_);
    }

    bool SetDuty(float percent)
    {
        if (percent <= 0 || period_ <= 0)
        {
            return false;
        }

        int dutyCycle = period_ * (percent / 100.0f);
        return WriteInt(dutyCyclePath_, dutyCycle);
    }

private:
    static bool WriteInt(const std::string &path, int value)
    {
        int fd = open(path.c_str(), O_WRONLY);
        if (fd < 0)
        {
            printf("open %s failed: %s!\n", path.c_str(), strerror(errno));
            return false;
        }

        std::string str = std::to_string(value);
        if (write(fd, str.data(), str.size()) < 0)
        {
            printf("write %d to %s failed: %s\n", value, path.c_str(), strerror(errno));
        }

        if (close(fd) < 0)
        {
            printf("close %s failed: %s\n", path.c_str(), strerror(errno));
            return false;
        }

        // printf("write %d to %s: OK!\n", value, path.c_str());
        return true;
    }

    bool Prepare()
    {
        if (chip_ < 0)
        {
            return false;
        }

        if (perpared_)
        {
            return true;
        }

        std::string path = "/sys/class/pwm/pwmchip" + std::to_string(chip_);
        exportPath_ = path + "/export";
        unexportPath_ = path + "/unexport";

        path += "/pwm" + std::to_string(chan_);
        enablePath_ = path + "/enable";
        periodPath_ = path + "/period";
        dutyCyclePath_ = path + "/duty_cycle";
        perpared_ = true;
        return true;
    }

    bool Export(bool on)
    {
        if (!Prepare())
        {
            return false;
        }

        return WriteInt(on ? exportPath_ : unexportPath_, chan_);
    }

    bool Enable(bool on)
    {
        if (!Prepare())
        {
            return false;
        }

        return WriteInt(enablePath_, on ? 1 : 0);
    }

private:
    int chip_ = -1;
    int chan_ = 0;   // 默认只用了 channel 0
    int period_ = 0;
    bool perpared_ = false;
    std::string exportPath_;
    std::string unexportPath_;
    std::string enablePath_;
    std::string periodPath_;
    std::string dutyCyclePath_;
};

这里实现了一个通用的PwmController类,可以非常方便的操作一个PWM芯片的某个通道。

3.4 实现呼吸灯效果

使用上面的PwmController可以非常方便的实现呼吸灯效果,代码如下:

void TestPwm1(int id)
{
    PwmController pwm(id);

    printf("pwm%d test start ...\n", id);
    pwm.SetFreq(100);
    pwm.Start();
    printf("pwm%d duty ratio up ...\n", id);
    for (int i = 0; i < 100; i++)
    {
        pwm.SetDuty(i);
        usleep(10 * 1000);
    }

    printf("pwm%d duty ratio down ...\n", id);
    for (int i = 100; i >= 0; i--)
    {
        pwm.SetDuty(i);
        usleep(10 * 1000);
    }

    pwm.Stop();
    printf("pwm%d test done!\n", id);
}

这段代码中,第一个for循环逐渐增加PWM占空比,实现亮度递增,第二个循环实现逐渐降低PWM占空比,亮度递减。

四、效果演示

这里是使用上面的TestPwm1函数,分别控制多彩发光板红、绿、蓝颜色的代码:

int main(int argc, char *argv[])
{
    TestPwm1(0);
    TestPwm1(1);
    TestPwm1(2);
    return 0;
}

演示效果见本贴末尾视频。

五、完整代码

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <functional>

class PwmController
{
public:
    PwmController(int chip, int chan = 0) : chip_(chip)
    {
        Export(true);
    }

    ~PwmController()
    {
        Export(false);
    }

    bool Start()
    {
        return Enable(true);
    }

    bool Stop()
    {
        return Enable(false);
    }

    bool SetFreq(int freq)
    {
        if (freq <= 0)
        {
            return false;
        }

        period_ = 1000000000uL / freq;
        return WriteInt(periodPath_, period_);
    }

    bool SetDuty(float percent)
    {
        if (percent <= 0 || period_ <= 0)
        {
            return false;
        }

        int dutyCycle = period_ * (percent / 100.0f);
        return WriteInt(dutyCyclePath_, dutyCycle);
    }

private:
    static bool WriteInt(const std::string &path, int value)
    {
        int fd = open(path.c_str(), O_WRONLY);
        if (fd < 0)
        {
            printf("open %s failed: %s!\n", path.c_str(), strerror(errno));
            return false;
        }

        std::string str = std::to_string(value);
        if (write(fd, str.data(), str.size()) < 0)
        {
            printf("write %d to %s failed: %s\n", value, path.c_str(), strerror(errno));
        }

        if (close(fd) < 0)
        {
            printf("close %s failed: %s\n", path.c_str(), strerror(errno));
            return false;
        }

        // printf("write %d to %s: OK!\n", value, path.c_str());
        return true;
    }

    bool Prepare()
    {
        if (chip_ < 0)
        {
            return false;
        }

        if (perpared_)
        {
            return true;
        }

        std::string path = "/sys/class/pwm/pwmchip" + std::to_string(chip_);
        exportPath_ = path + "/export";
        unexportPath_ = path + "/unexport";

        path += "/pwm" + std::to_string(chan_);
        enablePath_ = path + "/enable";
        periodPath_ = path + "/period";
        dutyCyclePath_ = path + "/duty_cycle";
        perpared_ = true;
        return true;
    }

    bool Export(bool on)
    {
        if (!Prepare())
        {
            return false;
        }

        return WriteInt(on ? exportPath_ : unexportPath_, chan_);
    }

    bool Enable(bool on)
    {
        if (!Prepare())
        {
            return false;
        }

        return WriteInt(enablePath_, on ? 1 : 0);
    }

private:
    int chip_ = -1;
    int chan_ = 0;   // 默认只用了 channel 0
    int period_ = 0;
    bool perpared_ = false;
    std::string exportPath_;
    std::string unexportPath_;
    std::string enablePath_;
    std::string periodPath_;
    std::string dutyCyclePath_;
};

void TestPwm1(int id)
{
    PwmController pwm(id);

    printf("pwm%d test start ...\n", id);
    pwm.SetFreq(100);
    pwm.Start();
    printf("pwm%d duty ratio up ...\n", id);
    for (int i = 0; i < 100; i++)
    {
        pwm.SetDuty(i);
        usleep(10 * 1000);
    }

    printf("pwm%d duty ratio down ...\n", id);
    for (int i = 100; i >= 0; i--)
    {
        pwm.SetDuty(i);
        usleep(10 * 1000);
    }

    pwm.Stop();
    printf("pwm%d test done!\n", id);
}

void TestPwm2(int id1, int id2)
{
    PwmController pwm1(id1);
    PwmController pwm2(id2);

    printf("pwm%d,%d test start ...\n", id1, id2);
    pwm1.SetFreq(10000);
    pwm1.Start();

    pwm2.SetFreq(10000);
    pwm2.Start();

    for (int i = -100; i <= 100; i += 10) {
        pwm1.SetDuty(100 - std::abs(i));
        for (int j = 0; j < 100; j++) {
            pwm2.SetDuty(j);
            usleep(10*1000);
        }
        for (int j = 100; j >= 0; j--) {
            pwm2.SetDuty(j);
            usleep(10*1000);
        }
    }

    pwm1.Stop();
    pwm2.Stop();
    printf("pwm%d,%d test done!\n", id1, id2);
}

int main(int argc, char *argv[])
{
    int ncycle = argc > 1 ? atoi(argv[1]) : 1;

    for (int i = 0; i < ncycle; i++)
    {
        TestPwm1(0);
        TestPwm1(1);
        TestPwm1(2);

        TestPwm2(0, 1); // RG
        TestPwm2(0, 2); // RB
        TestPwm2(1, 2); // GB
    }

    return 0;
}

编译命令:

loongarch64-linux-gnu-g++ pwm_rgb_led.cpp -o pwm_test -Wall

将生成的pwm_test通过FTP或者U盘拷贝到龙芯2K0500开发板上之后,运行:

./pwm_test

六、参考链接

  1. RGB LCD多彩发光板-LED灯-DFRobot创客商城
  2. Pulse Width Modulation (PWM) interface — The Linux Kernel documentation

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

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

相关文章

实现一个简单的录制软件:支持录制桌面与窗口

环境搭建 CSDN 将data文件与obs-plugins文件夹复制到bin/win32文件下 VS2019安装Qt插件&#xff08;附安装失败解决方案&#xff09;_振华OPPO的博客-CSDN博客 插件; 链接&#xff1a;https://pan.baidu.com/s/1fdNDJwrwrJ1SA0Q9AiM7qA?pwdiz4f 提取码&#xff1a;iz4f vs…

uniapp 微信小程序导航功能(从地址列表内点击某一个地址)

效果图&#xff1a; <template><view class"user"><view class"list"><view class"title">地址列表</view><view class"title-label"><view>名称</view><view>距离&#xff…

开启Windows共享文件夹审核,让用户查看谁删除了文件

在动画行业有个常用到的需求&#xff0c; 我的共享文件夹内的文件被谁删除了&#xff0c;查不到&#xff0c;只能查看谁创建&#xff0c;谁修改的&#xff0c;但查不到谁删除的&#xff0c;分享一下&#xff1a; 1 开始->运行->gpedit.msc 开发本地组策略编辑器, 在计算…

el-select 下拉选择框添加字段单位显示 el-select下拉按钮前加单位显示

背景&#xff1a;el-select可以通过自定义模版在下拉选项内加单位但是选择后没法显示单位 实现效果 实现代码 html <el-selectv-model"form.day"class"select-prefix"><el-option label"1" :value"1" /><el-option la…

Spark SQL 6-7

6. Spark SQL实战 6.1 数据说明 数据集是货品交易数据集。 每个订单可能包含多个货品&#xff0c;每个订单可以产生多次交易&#xff0c;不同的货品有不同的单价。 6.2 加载数据 tbStock&#xff1a; scala> case class tbStock(ordernumber:String,locationid:String,…

基于Java+vue前后端分离餐厅点菜管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Ext JS 如何设置工具栏按钮和一般按钮保持统一样式

在Ext JS 中, Button的背景色保持和系统的主色调一致, 样式如下: 但是使用工具栏(toolbar) 添加按钮的时候, 按钮的背景色确实灰色,如下图所示: 为什么会有这个差别呢? 如何让它们保持一致呢? 工具栏按钮与Button不一致的原因 看一下Toolbar里面的按钮最终产生…

C++中,C::C::C::C::foo() 为什么编译成功?

有人问&#xff1a; class Entity { public:static void foo() {} };int main() {Entity::Entity::Entity::Entity::Entity::foo(); } 为什么 最后那行&#xff1a; Entity::Entity::Entity::Entity::Entity::foo(); 能编译成功&#xff1f;这是什么规则&#xff1f; 嗯……

如何优雅的跳出 for 循环

文章目录 需求分析1. 普通for循环2. for..in循环3. for..of循环(ES6)4. forEach(callbackFn, ?thisArg)方法(ES5.1) 源码1. 终止 普通 for 循环2. 终止 forEach2.1 forEach 可以跳出本次循环&#xff0c;执行下一次循环2.2 forEach终止循环 需求 如何做到优雅的跳出 for 循环 …

像考研一样学个宇宙之刷题篇:剑指offerⅡ:整数系列——整数除法0706 TODO

001. 整数除法&#xff1a; 给定两个整数 a 和 b &#xff0c;求它们的除法的商 a/b &#xff0c;要求不得使用乘号 ‘*’、除号 ‘/’ 以及求余符号 ‘%’ 。 一些知识点和思路 第一题&#xff0c;easy题&#xff0c;狠狠来了个下马威。 首先是 “被除数/除数”关于溢出的情…

| 交互式建模与学习:重建人类运动功能

在报告《交互式建模与学习&#xff1a;重建人类运动功能》中&#xff0c;清华大学副教授眭亚楠介绍了AI在重建人类运动功能时&#xff0c;从无模型学习&#xff08;model-free learning&#xff09;到基于模型学习&#xff08;model-based learning&#xff09;的转变&#xff…

剑指 Offer II . 删除链表的倒数第 n 个结点

给定一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a;[] 示例 3&#x…

【MySQL入门实战5】-Linux PRM 包安装MySQL

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61…

【QT】QCustomPlot开发笔记

QCustomPlot开发 01、QCustomPlot简介1.1 帮助文档1.2 下载&使用 02、QCustomPlot项目使用笔记2.1 创建QCustomPlot 03、源代码 01、QCustomPlot简介 QCustomPlot 是一个用于科学绘图的 QT 第三方库&#xff0c;可以用于常见的二维图像绘制&#xff0c;比如函数曲线、参数…

管理类联考——择校——学费

截止2023年06月&#xff0c;关于广东的管理类联考的xuefei。 借鉴&#xff1a;https://zhuanlan.zhihu.com/p/421296334。罗列985相关院校 广州以本土MBA为主&#xff0c;共有9家院校&#xff1b; 深圳以外地MBA为主&#xff0c;有超过20家院校。 一梯队&#xff1a;北大光华…

日志---spdlog

spdlog中各对象都分为多线程与单线程版本&#xff1a; *_st&#xff1a;单线程版本&#xff0c;不用加锁&#xff0c;效率更高。*_mt&#xff1a;多线程版本&#xff0c;用于多线程程序是线程安全的。 spdlog是基于C11实现的一款纯头文件的日志管理库 git地址&#xff1a;htt…

WSI-finetuning

一、贡献 (1)通过引入一个IB模块来提出WSI-MIL的简单代理任务&#xff0c;该模块将包中超过10k个冗余实例提炼成不到1k个最受支持的实例。因此&#xff0c;在千兆像素图像上进行基于梯度的训练的并行计算成本减轻了十倍以上。通过对简化袋的学习和分类&#xff0c;发现由于病理…

【自学笔记】在SQL Server中创建用户角色及授权(使用SQL语句)更新2023.07.06

--<在SQL Server中创建用户角色及授权(使用SQL语句)>更新2023.07.06 --1. 首先在 SQL Server 服务器级别&#xff0c;创建登陆帐户&#xff08;create login&#xff09; --2. 创建数据库用户&#xff08;create user&#xff09;&#xff1a; --3. 通过加入数据库角色&a…

不忘初心,筑梦未来 | 【2023 ACDU 中国行·深圳站】数据库主题交流活动成功举办!

6月30日下午&#xff0c;【ACDU 中国行深圳站】在深圳回酒店圆满落下帷幕。本次活动由中国数据库联盟&#xff08;ACDU&#xff09;联合墨天轮社区主办&#xff0c;围绕「数据库前沿技术揭秘及应用」这一主题&#xff0c;七位数据库行业的领军人物从数据库新特性解读、创新与应…

Docker集群部署-redis集群

学习要求 利用Docker实现redis 集群的部署&#xff0c;实现3主3从集群配置&#xff0c;并在此基础上实现主从扩容、缩容。 学习准备 要求实验主机能够连接外网&#xff0c;已经正确安装Docker&#xff0c;并关闭防火墙和selinux。 学习步骤 创建6个docker容器实例&#xf…