首页 > 系统相关 >通过LINUX驱动控制FPGA端PWM外设(LED) 通过应用程序命令传参随意修改寄存器的值(PWM波频率和占空比随意修改)

通过LINUX驱动控制FPGA端PWM外设(LED) 通过应用程序命令传参随意修改寄存器的值(PWM波频率和占空比随意修改)

时间:2024-01-29 15:26:22浏览次数:24  
标签:随意 PWM ret char include cdev 占空比 pwm data

用法:

先下发下面的命令让kernel信息打印到串口:

 echo 7       4       1      7 > /proc/sys/kernel/printk

然后增加程序可执行性:

chmod 777 pwmdriver_app

 

 

先执行 ./pwmdriver_app /dev/pwm 400000 200 然后执行./pwm_driver_app /dev/pwm 400000 200, 可以发现LED[1]明显变暗。

 

底层硬件设计

module av_pwm(
    input clk,
    input reset_n,
    input as_read,
    input as_address,
    input as_write,
    output reg [31:0] as_readdata,
    input [31:0] as_writedata,
    output reg led);

reg [31:0]cnt_freq;
reg [31:0]cnt_duty;

reg    [31:0]    cnt0;
wire                end_cnt0;
//写pwm频率寄存器
always@(posedge clk or negedge reset_n)
    if(!reset_n) begin
        cnt_freq <= 32'd0;
    end
    else if(as_write && (as_address == 0))begin
        cnt_freq <= as_writedata;
    end
    else begin
        cnt_freq <= cnt_freq;
    end
    
//写pwm占空比寄存器
always@(posedge clk or negedge reset_n)
    if(!reset_n) begin
        cnt_duty <= 32'd0;
    end
    else if(as_write && (as_address == 1)) begin
        cnt_duty <= as_writedata;
    end
    else begin
        cnt_duty <= cnt_duty;
    end

//读寄存器
always@(posedge clk or negedge reset_n)
    if(!reset_n)
        as_readdata <= 32'd0;
    else if(as_read)begin
        case(as_address)
            0:as_readdata <= cnt_freq;
            1:as_readdata <= cnt_duty;
            default:as_readdata <= 32'd0;
        endcase
    end

always @(posedge clk or negedge reset_n)begin
    if(!reset_n)begin
        cnt0 <= 0;    
    end
    else begin
        if(end_cnt0)begin
            cnt0 <= 0;end
        else begin
            cnt0 <= cnt0 + 1;
        end
    end
end

assign end_cnt0 = (cnt0==cnt_freq -1);

always @(posedge clk or negedge reset_n)begin
    if(reset_n==1'b0)begin
        led <= 1;
    end
    else if(cnt0==cnt_duty-1)begin
        led <= 0;
    end
    else if(end_cnt0)begin
        led <= 1;
    end
end
endmodule
 

 

底层Linux驱动设计

#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/fs.h> 
#include <linux/cdev.h> 
#include <linux/device.h> 
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/list.h>
#include <linux/uaccess.h> 
//#include <linux/version.h> 
//#include <linux/slab.h> 
#include <asm/io.h> 
#include "hps_0.h" 
/***************************************************************
文件名: pwmdriver.c
作者: Doreen
版本: V1.0 2024/01/04
描述: pwm (led)驱动文件
博客:https://blog.csdn.net/weixin_47841246?spm=1000.2115.3001.5343
知乎: https://www.zhihu.com/people/fen-dou-de-nian-qing-ren-1
论坛: http://www.myfpga.org
***************************************************************/

/* 寄存器物理地址 */
#define H2F_LW_BASE_ADDR    (0xFF200000) 
//#define PWM_BASE 0x6000
#define MAP_PWM_ADDR         (H2F_LW_BASE_ADDR + PWM_BASE) 
#define PWM_SPAN 16

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *LWH2F_bridge_addr;
/* pwm设备结构体 */
struct pwm_dev{

    dev_t devid;            /* 设备号 */
    struct cdev cdev;       /* cdev */
    struct class *class;    /* 类  */
    struct device *device;  /* 设备 */
    int major;              /* 主设备号 */
    int minor;              /* 次设备号 */
    //unsigned int cnt_freq; /*pwm频率寄存器*/
    //unsigned int cnt_duty; /*pwm占空比寄存器*/

};

struct pwm_dev pwm; /* pwm设备 */

/*定义设备名称*/
#define DEVICE_NAME "pwm" 

static int pwm_open(struct inode *inode, struct file *filp ) 
{ 
    //try_module_get(THIS_MODULE); 
    filp->private_data = &pwm; /* 设置私有数据 */
   // printk(KERN_WARNING "pwm_open()\r\n"); 
    return 0; 
} 

static int pwm_release(struct inode *inode, struct file *filp ) 
{ 
   // module_put(THIS_MODULE); 
    filp->private_data = NULL;
    //printk("pwm_close()\n"); 
    return 0; 
}
ssize_t pwm_read(struct file *filp, char *buf, size_t count,loff_t *f_pos)
{
int ret,register_data_1,register_data_2;
char char_data[32];
//printk("kernel pwm_read\n");
register_data_1 = ioread32(LWH2F_bridge_addr + 0);/*读 PWM 寄存器0,即 cnt_freq 寄存器*/
register_data_2 = ioread32(LWH2F_bridge_addr + 4);/*读 PWM 寄存器1,即 cnt_duty 寄存器*/
/*将读取到的数据拷贝到用户空间*/
sprintf(char_data,"%d %d",register_data_1,register_data_2);
ret = copy_to_user(buf, char_data, sizeof(char_data));
printk("register_data_1 = %d; copy_to_user = %s; \n",register_data_1,char_data);
if (ret !=0)
{
return -EFAULT;
}
//printk("read done\n");
return count;
}

ssize_t pwm_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{

int ret;
int data_1;
int data_2;
char char_data[32]="";
//printk("kernel pwm_write\n");
/*从用户空间将数据拷贝到 char_data 数组中*/
ret=copy_from_user(char_data, buf, 32);
printk("copy_from_user=%s \n",char_data);
if (ret !=0)
{
return -EFAULT;
}

sscanf(char_data,"%d %d",&data_1,&data_2);
iowrite32(data_1, LWH2F_bridge_addr + 0);/*写 PWM 寄存器 0,即 cnt_freq 寄存器*/
iowrite32(data_2, LWH2F_bridge_addr + 4);/*写 PWM 寄存器 1,即 cnt_duty 寄存器*/
printk("kernel set pwm_freq:%d; pwm_duty:%d\n",data_1, data_2);
//printk("write done\n");

return count;
}

 /*定义驱动程序操作函数*/
struct file_operations pwm_fops =
{
    .owner = THIS_MODULE,
    .open = pwm_open,
    .write = pwm_write,
    .read = pwm_read,
    .release = pwm_release,
};

/* 驱动入口函数 */
static int __init pwm_init(void)
{
   // u32 val = 0;
    int ret;
    LWH2F_bridge_addr = ioremap_nocache(MAP_PWM_ADDR, PWM_SPAN);
    
    /* 注册字符设备驱动 */
    /* 1、创建设备号 */
    if (pwm.major) {      /*  定义了设备号 */
        pwm.devid = MKDEV(pwm.major, 0);
        ret = register_chrdev_region(pwm.devid, 1, DEVICE_NAME);
        if(ret < 0) {
            pr_err("cannot register %s char driver [ret=%d]\n",DEVICE_NAME, 1);
            goto fail_map;
        }
    } else {              /* 没有定义设备号 */
        ret = alloc_chrdev_region(&pwm.devid, 0, 1, DEVICE_NAME);  /* 申请设备号 */
        if(ret < 0) {
            pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", DEVICE_NAME, ret);
            goto fail_map;
        }
        pwm.major = MAJOR(pwm.devid);   /* 获取分配号的主设备号 */
        pwm.minor = MINOR(pwm.devid);   /* 获取分配号的次设备号 */
    }
    printk(KERN_ERR "pwm major=%d,minor=%d\r\n",pwm.major, pwm.minor);
   
    /* 2、初始化cdev */
    pwm.cdev.owner = THIS_MODULE;
    cdev_init(&pwm.cdev, &pwm_fops);
   
    /* 3、添加一个cdev */
    ret = cdev_add(&pwm.cdev, pwm.devid, 1);
    if(ret < 0)
        goto del_unregister;
       
    /* 4、创建类 */
    pwm.class = class_create(THIS_MODULE, "pwm");
    if (IS_ERR(pwm.class)) {
        goto del_cdev;
    }
    /* 5、创建设备 */
    pwm.device = device_create(pwm.class, NULL, pwm.devid, NULL, DEVICE_NAME);
    if (IS_ERR(pwm.device)) {
        goto destroy_class;
    }
   
    return 0;
    

/* 注销字符设备 */
del_cdev:
    cdev_del(&pwm.cdev);
    
/* 销毁类 */
destroy_class:
    class_destroy(pwm.class);
    
/* 注销设备号 */
del_unregister:
    unregister_chrdev_region(pwm.devid, 1);    
    
/* 释放内存映射 */
fail_map:
    iounmap(LWH2F_bridge_addr);/*取消虚拟地址映射*/    
    return -EIO;
}

/*驱动出口函数*/
static void __exit pwm_exit(void)
{
    /* 取消映射 */
    iounmap(LWH2F_bridge_addr);
    /* 注销字符设备 */
    cdev_del(&pwm.cdev);
    /* 注销设备号 */
    unregister_chrdev_region(pwm.devid, 1); 
    /* 销毁设备 */
    device_destroy(pwm.class, pwm.devid);
    /* 销毁类 */
    class_destroy(pwm.class);
}

module_init(pwm_init);
module_exit(pwm_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Doreen"); 

 

应用程序设计:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

/***************************************************************
文件名 : pwmdriver_app.c
作者 : Doreen
版本 : V1.0
描述 : pwm 测试 应用程序。
使用方法 :./pwmdriver_pp /dev/pwm 400000 200000 
(第三个400000是pwm波频率 第四个参数200000 是pwm占空比 50%)
博客:https://blog.csdn.net/weixin_47841246?spm=1000.2115.3001.5343
知乎: https://www.zhihu.com/people/fen-dou-de-nian-qing-ren-1
论坛: http://www.myfpga.org
***************************************************************/

/*
* @description : main 主程序
* @param - argc : argv 数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
    int ret = 0;
    int fd = 0;
    char *filename;
    char data_w[32]="";
    //char *data_w[2];
    char data_r[32];
    if(argc != 4){
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];
    //data_w[0] = argv[2];
    //data_w[1] = argv[3];

    /* 打开 pwm 驱动 */
    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("file %s open failed!\r\n", argv[1]);
        return -1;
    }
    /*调用read方法读取/dev/pwm设备的寄存器数据到用户空间的data数组中*/
    
    /* 向/dev/pwm 文件写入数据 */
    strcat(data_w,argv[2]);
    strcat(data_w," ");
    strcat(data_w,argv[3]);
    printf("parameters from command are = %s\r\n",data_w);
    ret = write(fd, data_w, sizeof(data_w));
    if(ret < 0){
        printf("User write Failed!\r\n");
        close(fd);
        return -1;
    }
    else {/*打印你写入的数据*/
        printf("write successfully\r\n");
    }
    
    usleep( 100*1000 );

    /* 从/dev/pwm 文件读出数据 */
    ret = read(fd,data_r,32);
    if(ret < 0){
        printf("read error \n");
    return -1;}
    else {/*打印读取到的数据*/
    //printf("read_byte_number:%d; \r\n",ret);
    printf("user read %s;\r\n",data_r);
    //printf("Read pwm_freq:%s; pwm_duty:%s;\r\n",data_r[0],data_r[1]);
    }
    
    /* 关闭文件 */
    ret = close(fd); 
    if(ret < 0){
        printf("file %s close failed!\r\n", argv[1]);
        return -1;
    }
    return 0;
}

 

标签:随意,PWM,ret,char,include,cdev,占空比,pwm,data
From: https://www.cnblogs.com/DoreenLiu/p/17994578

相关文章

  • 定时器PWM控制RGB彩灯案例
    1.脉冲宽度调制PWM    PWM(PulseWidthModulation)简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在测量、通信、工控等方面。   PWM的一个优点是从处理器到被控系统信号都是数字形式的,再进行数模转换。可将噪声影响降到最低......
  • 06 PWM呼吸灯实验
    软件版本:VIVADO2021.1操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录米联客(MILIANKE)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!1概述小小呼吸灯在很多设备上我们可以看到,我们可以在FPGA的控制主板上也可以加入呼吸灯,指示我们的FPGA主板......
  • 定时器PWM输出实验
    定时器PWM输出实验由于是用TIM定时器输出,必涉及到IO口的复用。原理:以上计数方式为例:计数器从零计数到arr(重装载值),此过程中,我们定义一个crr_x,则被分为两种情况:计时器值大于crr_x,计数器值小于crr_x。我们使第二种情况输出低电平,第一种情况输出高电平,就可以得到输出电平。我们调整a......
  • LED洗墙灯光源驱动芯片AP5199S降压恒流pwm调光IC
    说明AP5199S是一款外围电路简单的多功能平均电流型 LED 恒流驱动器,适用于宽电压范围的非隔离式大功率恒流 LED 驱动领域。芯片PWM端口支持超小占空比的PWM调光,可响应最小 60ns 脉宽。芯片采用我司专利算法,为客户提供最佳解决方案,最大限度地发挥灯具优势,以实现景观舞台......
  • LED洗墙灯光源驱动芯片AP5199S降压恒流pwm调光IC
    说明AP5199S是一款外围电路简单的多功能平均电流型 LED 恒流驱动器,适用于宽电压范围的非隔离式大功率恒流 LED 驱动领域。芯片PWM端口支持超小占空比的PWM调光,可响应最小 60ns 脉宽。芯片采用我司专利算法,为客户提供最佳解决方案,最大限度地发挥灯具优势,以实现景观舞台灯......
  • STM32学习笔记_PWM驱动舵机和直流电机(4)
    STM32驱动舵机:舵机接线图:将PWM脚接在开发板PA1处;新建舵机模块:分析舵机的PWM信号:在一个周期20ms的脉冲信号上,高电平占空比为0.5ms-2.5ms分别对应着0°-180°。计算PWM的参数信息:PSC为72,ARR为20K,这样就得到一个频率周期为50Hz的信号。其中20KHz等于20ms,将CCR设置为500即=0.5ms的占空......
  • 带PWM 调光的线性降压 LED 恒流驱动器
    一、基本概述TX6410B是一种带PWM调光功能的线性降压LED恒流驱动器,仅需外接一个电阻就可以构成一个完整的LED恒流驱动电路,调节该外接电阻可调节输出电流,输出电流范围为10~2000mA。TX6410B内置30V50毫欧MOS。TX6410B内置过热保护功能,可有效保护芯片,避免因过热而造成损坏。......
  • PWM 调光的线性降压 LED 恒流驱动器
    一、基本概述TX6410是一种带PWM调光功能的线性降压LED恒流驱动器,仅需外接一个电阻就可以构成一个完整的LED恒流驱动电路,调节该外接电阻可调节输出电流,输出电流范围为10~2000mA。内置30V50毫欧MOS。内置过热保护功能,可有效保护芯片,避免因过热而造成损坏。具有很低的静态......
  • 51单片机--PWM
    芯片:STC8H1K28目的:呼吸灯这是一段官网下载的例程,配置了多路PWM,目前不知道具体寄存器赋值,先把能使用的代码贴上,后续再添加#defineTimer0_Reload(65536UL-(MAIN_Fosc/1000))//Timer0中断频率,1000次/秒#definePWMA_ENO(*(unsignedcharvolatilexdat......
  • Linux PWM接口概述 【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/driver-api/pwm.htmlLinuxPWM接口概述PWM(脉冲宽度调制)接口用于控制LED、风扇或手机中的振动器。具有固定目的的PWM无需实现LinuxPWMAPI(尽管它们可以)。然而,在SoC上通常会发现作为离散设备的PWM,它们没有固定的用途。将它们连接到LED或......