当前位置: 首页 > news >正文

RK3568平台(PWM篇)PWM驱动

一.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);    // 启动 PWMreturn 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);    // 停止 PWMreturn 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;  // 程序执行成功
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • xss-靶场
  • 基于协同过滤算法的体育商品推荐系统_t81xg
  • 气膜游泳馆:夏日避暑的绝佳选择—轻空间
  • Perl(Practical Extraction and Reporting Language)脚本
  • 自主身份:Web3如何重新定义个人数据所有权
  • 基于 Spring Boot 的快速开发微信公众平台的框架-FastBootWeixin框架
  • RabbitMQ-消息队列之topic使用
  • Linux目录结构及基础查看命令和命令模式
  • EmguCV学习笔记 VB.Net 4.5 像素距离和连通区域
  • ECCV2024|商汤发布3D面部动画系统UniTalker:通过统一模型扩展音频驱动的 3D 面部动画
  • Verilog刷题笔记55
  • 第4章 汇编语言和汇编软件
  • MySQL索引的性能优化
  • ES6-ES13学习笔记
  • 【机器学习】逻辑回归原理(极大似然估计,逻辑函数Sigmod函数模型详解!!!)
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • [译]如何构建服务器端web组件,为何要构建?
  • 【翻译】babel对TC39装饰器草案的实现
  • const let
  • Git同步原始仓库到Fork仓库中
  • HashMap ConcurrentHashMap
  • input实现文字超出省略号功能
  • leetcode386. Lexicographical Numbers
  • oschina
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • 闭包,sync使用细节
  • 工作中总结前端开发流程--vue项目
  • 计算机在识别图像时“看到”了什么?
  • 力扣(LeetCode)357
  • 如何打造100亿SDK累计覆盖量的大数据系统
  • 线性表及其算法(java实现)
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • #include到底该写在哪
  • #传输# #传输数据判断#
  • #前后端分离# 头条发布系统
  • (MTK)java文件添加简单接口并配置相应的SELinux avc 权限笔记2
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (Python第六天)文件处理
  • (二)Eureka服务搭建,服务注册,服务发现
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (五)IO流之ByteArrayInput/OutputStream
  • (译)计算距离、方位和更多经纬度之间的点
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • ***详解账号泄露:全球约1亿用户已泄露
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • *1 计算机基础和操作系统基础及几大协议
  • .Net Core 中间件验签
  • .NET Core中的去虚
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .NET 指南:抽象化实现的基类
  • .net和php怎么连接,php和apache之间如何连接
  • .net连接MySQL的方法