RK3568平台(PWM篇)PWM驱动

news2024/11/24 20:04:27

一.PWM基础知识

PWM 全称为 Pulse Width Modulation,翻译成中文为脉冲宽度调制,它是一种数字信号控 制模拟电路的技术,可以通过改变高/低电平的占空比来控制平均电压或功率,从而达到对模拟 量的控制目的。

周期(T):指一个完整的高低电平循环所需要的时间,而频率为周期的倒数,指在 1 秒钟有多 少个周期,单位为 Hz,例如一个周期是 20ms,那么一秒钟就有 50 次 PWM 周期。

占空比(Duty Cycle):是指高电平时间与周期的比例,通常以百分比表示,例如周期为 20ms, 高电平所占的时间为 10ms,那占空比就是 50%。

硬件PWM和软件PWM:

硬件 PWM:

(1)实现方式:硬件 PWM 是由专门的 PWM 硬件模块实现 PWM 输出的方式。

(2)优点: ·CPU 占用低,PWM 输出由硬件模块自动完成,无需 CPU 介入。 ·PWM 输出频率和分辨率高,可以达到高达 MHz 级的频率和 ns 级的分辨率。 ·输出波形稳定可靠,不易受 CPU 负载的影响。

(3)缺点: ·需要专门的硬件 PWM 模块,成本较高。 ·PWM 输出引脚受限,只能在预定义的引脚上输出。

软件 PWM:

(1)实现方式:软件 PWM 是通过软件编程实现 PWM 输出的方式。利用定时器中断或者 循环计数的方式,在软件中控制输出引脚的高低电平切换时间,从而生成 PWM 波形。

(2)优点: ·灵活性强,可以在任意 GPIO 引脚上生成 PWM 波形。 ·成本低,不需要额外的硬件 PWM 模块。

(3)缺点: ·CPU 占用较高,因为需要在中断服务程序或者循环中实时控制引脚电平。 ·PWM 输出的频率和分辨率受 CPU 主频和中断响应时间的影响,无法高频高分辨率。 ·对 CPU 性能和实时性要求较高。

二.PWM 子系统框架

 PWM 子系统被划分为了三个层次,分别为用户空间、内核空间和硬件层,内核 空间包括 PWM 设备驱动层、PWM 核心层和 PWM 适配器驱动层。

pwm 子系统 API 讲解:

pwm_config 函数:

函数原型: int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);

函数作用: pwm_config 用于改变 PWM 的配置,包括占空比和周期。这是 PWM 子系统中最常用的函 数之一,用于调整 PWM 信号的关键参数。

函数参数: struct pwm_device *pwm: 指向 PWM 设备结构体的指针。 int duty_ns: 占空比,单位是纳秒,表示高电平持续的时间。 int period_ns: 周期,单位是纳秒,表示 PWM 信号的总周期。

pwm_set_polarity 函数:

函数原型:int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity);

函数作用: 设置 PWM 信号的极性。PWM 信号可以是正极性或负极性,这决定了信号的高低电平的 定义。

函数参数: struct pwm_device *pwm: 指向 PWM 设备结构体的指针。 enum pwm_polarity polarity: 极性参数,可以是 PWM_POLARITY_NORMAL(正极性)或 PWM_POLARITY_INVERSED(负极性)。

pwm_enable 函数:

函数原型:int pwm_enable(struct pwm_device *pwm);

函数作用: 使能 PWM 信号,即开始输出 PWM 信号。

函数参数: struct pwm_device *pwm: 指向 PWM 设备结构体的指针。

pwm_disable 函数:

函数原型:void pwm_disable(struct pwm_device *pwm);

函数作用: 关闭 PWM 信号,即停止输出 PWM 信号。

函数参数: struct pwm_device *pwm: 指向 PWM 设备结构体的指针。

devm_of_pwm_get 函数

函数原型:struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, const char *con_id);

函数作用: 从设备树节点获取 PWM 设备。 (3)函数参数: struct device *dev: 设备结构体。 struct device_node *np: 设备节点。 const char *con_id: 连接 ID。

三.PWM驱动舵机驱动程序

#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/uaccess.h>
#include <linux/pwm.h>

dev_t dev_num;
struct cdev cdev_test;
struct class *class;
struct device *device;

struct pwm_device *sg90_pwm_device;

// 打开设备时的回调函数
static int cdev_test_open(struct inode *inode, struct file *file) {
    printk("This is cdev test open\n");
    pwm_config(sg90_pwm_device, 500000, 20000000);    // 配置 PWM 参数:脉冲宽度为 50000000 纳秒, 周期为 20,000,000 纳秒
    pwm_set_polarity(sg90_pwm_device, PWM_POLARITY_NORMAL);    // 设置 PWM 极性为正常极性
    pwm_enable(sg90_pwm_device);    // 启动 PWM
    return 0;
}

// 写入设备时的回调函数
static ssize_t cdev_test_write(struct file *file, const char __user *buf, size_t size, loff_t *off) {
    int ret;
    unsigned char data[1];
    printk("This is cdev test write\n");
    // 从用户空间拷贝数据到内核空间
    ret = copy_from_user(data, buf, size);
    if (ret) {
        printk("copy_from_user failed\n");
        return -EFAULT;
    }
    // 更新 PWM 参数:脉冲宽度根据用户输入的数据调整
    pwm_config(sg90_pwm_device, 500000 + data[0] * 100000 / 9, 20000000);
    return size;
}

// 释放设备时的回调函数
static int cdev_test_release(struct inode *inode, struct file *file) {
    printk("This is cdev test release\n");

    pwm_config(sg90_pwm_device, 500000, 20000000);    // 回到初始的 PWM 参数配置
    pwm_disable(sg90_pwm_device);    // 停止 PWM
    return 0;
}

// 定义字符设备操作函数集合
static struct file_operations cdev_test_ops = {
    .owner = THIS_MODULE,
    .open = cdev_test_open,
    .write = cdev_test_write,
    .release = cdev_test_release,
};

// 设备探测函数
static int sg90_probe(struct platform_device *pdev) {
    int ret;

    // 获取 PWM 设备
    sg90_pwm_device = devm_pwm_get(&pdev->dev, NULL);
    if (IS_ERR(sg90_pwm_device)) {
        printk("Failed to get PWM device\n");
        return PTR_ERR(sg90_pwm_device);
    }

    // 申请设备号
    ret = alloc_chrdev_region(&dev_num, 0, 1, "alloc_name");
    if (ret < 0) {
        printk("alloc_chrdev_region is error\n");
        return ret;
    }
    printk("alloc_chrdev_region is ok\n");

    // 初始化字符设备
    cdev_init(&cdev_test, &cdev_test_ops);
    cdev_test.owner = THIS_MODULE;

    ret = cdev_add(&cdev_test, dev_num, 1);// 添加字符设备
    class = class_create(THIS_MODULE, "test");    // 创建设备类
    device = device_create(class, NULL, dev_num, NULL, "sg90");    // 创建设备

    printk("sg90_probe successful\n");
    return 0;
}

// 设备移除函数
static int sg90_remove(struct platform_device *pdev) {

    device_destroy(class, dev_num);    // 删除设备
    class_destroy(class);    // 删除设备类
    cdev_del(&cdev_test);    // 删除字符设备
    unregister_chrdev_region(dev_num, 1);    // 释放设备号
    printk("sg90_remove successful\n");
    return 0;
}

// 设备树匹配表
static const struct of_device_id sg90_of_device_id[] = {
    {.compatible = "sg90"},
    {},
};
MODULE_DEVICE_TABLE(of, sg90_of_device_id);

// 定义平台驱动
static struct platform_driver sg90_platform_driver = {
    .driver = {
        .name = "sg90",
        .of_match_table = sg90_of_device_id,
    },
    .probe = sg90_probe,
    .remove = sg90_remove,
};

// 模块初始化函数
static int __init modulecdev_init(void) {
    int ret;

    // 注册平台驱动
    ret = platform_driver_register(&sg90_platform_driver);
    if (ret) {
        printk("platform_driver_register is error\n");
        return ret;
    }
    printk("platform_driver_register is ok\n");

    return 0;
}

// 模块退出函数
static void __exit modulecdev_exit(void) {
    // 注销平台驱动
    platform_driver_unregister(&sg90_platform_driver);
    printk("bye bye\n");
}

// 声明模块许可证和作者
module_init(modulecdev_init);
module_exit(modulecdev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("topeet");
#include <fcntl.h>  
#include <unistd.h>  
#include <stdlib.h> 
#include <stdio.h> 

int main(int argc, char *argv[]) {
    // argc 表示命令行参数的数量,包括程序名本身
    // argv 是一个字符串数组,存储了各个命令行参数

    int fd;  // 文件描述符,用于标识打开的设备文件
    unsigned char buf[1];  // 存储要写入设备的单个字节数据

    // 如果命令行参数个数小于2,说明缺少要写入的值,打印用法并返回错误
    if (argc < 2) {
        printf("Usage: %s <value>\n", argv[0]);
        return -1;
    }

    // 以只写方式打开设备文件"/dev/sg90"
    fd = open("/dev/sg90", O_WRONLY);
    if (fd < 0) {  // 打开设备文件失败,输出错误信息并返回错误
        perror("open");
        return -1;
    }

    // 将命令行参数转换为整数,存储在buf[0]中
    buf[0] = (unsigned char)atoi(argv[1]);
    // 将buf中的1个字节数据写入打开的设备文件
    if (write(fd, buf, 1) != 1) {  // 写入失败,输出错误信息,关闭文件并返回错误
        perror("write");
        close(fd);
        return -1;
    }

    // 延迟3秒,模拟设备操作
    sleep(3);
    // 关闭设备文件
    close(fd);

    return 0;  // 程序执行成功
}

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

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

相关文章

Vue条件判断:v-if、v-else、v-else-if、v-show 指令

在程序设计中&#xff0c;条件判断是必不可少的技术。在视图中&#xff0c;经常需要通过条件判断来控制 DOM 的显示状态。Vue.js 提供了相应的指令用于实现条件判断&#xff0c;包括&#xff1a;v-if、v-else、v-else-if、v-show 指令。 1、v-if 指令 v-if 指令可以根据表达式…

机器学习 之 线性回归算法

目录 线性回归&#xff1a;理解与应用 什么是线性回归&#xff1f; 一元线性回归 正态分布的重要性 多元线性回归 实例讲解 数据准备 数据分析 构建模型 训练模型 验证模型 应用模型 代码实现 线性回归&#xff1a;理解与应用 线性回归是一种广泛使用的统计方法&…

企业高性能web服务器,原理及实例

一、Web 服务基础介绍 正常情况下的单次web服务访问流程&#xff1a; 1.1 Web 服务介绍 1993年3月2日&#xff0c;中国科学院高能物理研究所租用AT&T公司的国际卫星信道建立的接入美国SLAC国家实 验室的64K专线正式开通&#xff0c;成为我国连入Internet的第一根专线。 1…

Mycat分片-垂直拆分

目录 场景 配置 测试 全局表配置 续接上篇&#xff1a;MySQ分库分表与MyCat安装配置-CSDN博客 续接下篇&#xff1a;Mycat分片-水平拆分-CSDN博客 场景 在业务系统中, 涉及以下表结构 ,但是由于用户与订单每天都会产生大量的数据, 单台服务器的数据 存储及处理能力是有限…

0x01 GlassFish 任意文件读取漏洞复现

参考文章&#xff1a; 应用服务器glassfish任意文件读取漏洞 - SecPulse.COM | 安全脉搏 fofa 搜索使用该服务器的网站 网络空间测绘&#xff0c;网络空间安全搜索引擎&#xff0c;网络空间搜索引擎&#xff0c;安全态势感知 - FOFA网络空间测绘系统 "glassfish"&…

VUE3-nest前后端部署教程-----4.服务器linux中部署Node.js环境

一.安装分布式版本管理系统Git (Alibaba Cloud Linux 3/2、CentOS 7.x) sudo yum install git -y 二.使用Git将NVM的源码克隆到本地的~/.nvm目录下&#xff0c;并检查最新版本。 git clone https://gitee.com/mirrors/nvm.git ~/.nvm && cd ~/.nvm && gi…

仿Muduo库实现高并发服务器——Server.hpp框架的简单描述

EventLoop模块在本项目中的简单使用&#xff1a; 下面这张图 是channel模块&#xff0c;poller模块&#xff0c;TimerWheel模块&#xff0c;EventLoop模块&#xff0c;LoopThreadPool模块进行组合。便于大家对这个项目的理解&#xff0c;因为代码看起来挺复杂的。 下面这个图&…

2024河南萌新联赛第六场题解

这场的出题组是郑州大学acm实验室&#xff0c;验题组是郑州大学和郑州轻工业大学的志愿者同学们。 官方题解大部分内容是我写的&#xff0c;B题思路部分是对应的出题人自己写的&#xff0c;E题思路以及代码都是对应的出题人写的因为我不会写这个题&#xff0c;题目的题面&…

C++--类和对象(三)

本篇主要是填补前两篇类和对象中的小知识点 第一篇:C--类和对象&#xff08;一&#xff09;-CSDN博客 第二篇:C--类和对象&#xff08;二&#xff09;-CSDN博客 目录 1.初始化列表 2.友元&#xff08;friend&#xff09; 3.内部类 1.初始化列表 在之前实现构造函数的时候&a…

uniapp 总结

uniapp的 发送请求的 responseType是没有 blob这个类型的&#xff0c; responseType: ‘arraybuffer’, uniapp 标准js和浏览器js的区别 downloadFile的原理作用

从零开始学习SLAM(五):极几何与极约束

文章参考计算机视觉life 前备知识 概念 几何关系&#xff1a; 上图中&#xff1a; 极平面&#xff08;Epipolar plane&#xff09;&#xff1a;点c0, c1, p三点确定的平面&#xff1b; 极点&#xff08;Epipoles&#xff09;&#xff1a; c0 c1 连线与两个平面的交点 基线&a…

SAP Parallel Accounting(平行分类账业务)配置及操作手册【适用于多国家会计准则】

1. 配置准备 1.1 理解平行账概念 平行账&#xff0c;也称为多分类账&#xff0c;是SAP系统中的一项功能&#xff0c;它允许企业按照不同的会计准则来维护各自的财务数据。这种设置特别适用于那些需要符合多种会计准则的跨国公司。通过平行账&#xff0c;企业可以在不同的分类…

单片机存储芯片 W25QXX、AT24C02

一、FLASH W25QXX (1) W25QXX芯片简介 W25Q128是华邦公司推出的一款SPI接口的NOR Flash芯片&#xff0c;其存储空间为128Mbit&#xff0c;相当于16M字节。W25Q128V芯片是串行闪存&#xff0c;可以通过标准/两线/四线SPI控制。W25Q128一次最多可编程256个字节。页面可以按扇区擦…

特斯拉算法,暴力递归尝试,汉诺塔问题

改进之后的算法 暴力递归就是尝试 汉诺塔问题 主函数 打印一个字符串的全部子序列&#xff0c;包括空字符串

【数据结构题集(c语言版)】内部排序算法比较 题解(起泡排序+直接插入排序+简单选择排序+快速排序+希尔排序+堆排序)

内部排序算法比较 问题描述 在教科书中,各种内部排序算法的时间复杂度分析结果只给出了算法执行时间的阶或大概执行时间。试通过随机数据比较各算法的关键字比较次数和关键字移动次数,以取得直观感受。 基本要求 对以下6种常用的内部排序算法进行比较:起泡排序、直接插入排…

SpringBoot依赖之Spring Data Redis 实现地理坐标(Geospatial)

Spring Boot 项目中使用 Spring Data Redis 实现地理坐标(Geospatial) 概念 Spring Data Redis (AccessDriver) 依赖名称: Spring Data Redis (AccessDriver)功能描述: Advanced and thread-safe Java Redis client for synchronous, asynchronous, and reactive usage. Sup…

JVM 有哪些垃圾回收器?

JVM 有哪些垃圾回收器&#xff1f; 图中展示了7种作用于不同分代的收集器&#xff0c;如果两个收集器之间存在连线&#xff0c;则说明它们可以搭配使用。虚拟机所处的区域则表示它是属于新生代还是老年代收集器。 新生代收集器&#xff08;全部的都是复制算法&#xff09;&…

使用 Charles 模拟手机弱网测试

在移动互联网时代&#xff0c;网络状况的不确定性给应用程序带来了挑战。尤其是在偏远地区或信号不佳的地方&#xff0c;用户的网络连接可能会变得不稳定。因此&#xff0c;对应用程序进行弱网测试变得尤为重要。Charles Proxy 是一款广泛使用的网络调试工具&#xff0c;它不仅…

分享小诗梦404炫酷单页面html5源码

源码介绍 分享小诗梦404炫酷单页面html5源码&#xff0c;小诗梦的一个很炫酷页面&#xff0c;感觉应该符合一些人的感觉&#xff01;可以用来做404页面。 源码下载 分享小诗梦404炫酷单页面html5源码

C++ | Leetcode C++题解之第363题矩形区域不超过K的最大数值和

题目&#xff1a; 题解&#xff1a; class Solution { public:int maxSumSubmatrix(vector<vector<int>> &matrix, int k) {int ans INT_MIN;int m matrix.size(), n matrix[0].size();for (int i 0; i < m; i) { // 枚举上边界vector<int> sum(…