• 十四.PWM输出


    PWM的原理我这里就不再说了,脉冲宽度调制,通过改变周期和产空比满足负载不同的功率需求。

    I.MX6UL的PWM功能

    I.MX6UL的PWM和处理器内核对接的时候遵循外设总线协议,PWM和其他模块之间只有时钟信号(CCM模块)和重启信号(SRC模块)相关(还有中断处理),还有一个单独的输出信号。功能特性如下:

    • 16位的向上计数器、时钟源可选择。
    • 4X16位的FIFO,可以降低中断资源
    • 可配置的输出高低电平方式
    • 可以生产回滚或比较中断

    I.MX6UL提供了8组PWM,每组的输出信号可以在几个引脚之间通过复用配置选择。,整体的框架结构如下:

    I.MX6UL的PWM产生流程是这样的:

    时钟选择器决定了计数器的工作频率,和前面一样,我们使用外设时钟周期(IPG_CLK,66MHz)

    12位分频器,可以实现1~4097分频,假设我们选用66分频,每次counter计数的时候就是1us

    Period Resgiter,周期寄存器,16位,当计数器的值和周期寄存器的值相等时,产生中断(这个中断可以不生成,类似于溢出中断)。计数器值等于周期,从0开始重新计数。

    Sample Register,采样寄存器,16位。这个寄存器的值决定信号的占空比。这个比较重要,我们后面单独讲一下。但是意思就是计数器的值和SampleRegister的值相等时信号反转。

    SampleRegister和FIFO

    占空比是通过SampleRegister交给FIFO的,注意FIFO深度是4组,实际信号的占空比是通过FIFO的值去确定的,所以FIFO需要不停的写入新的值才能不断的生成新的信号。FIFO在任何时间都可以进行写操作,但要读取值必须在PWM使能的条件下。因为FIFO的深度是4,写入数据的时候要注意防止上溢,否则FWE异常(FIFO Write Error)。同理,我们还要检测FIFO的内部元素数量,防止FIFO空了无法生成新的信号。这个过程可以通过中断产生,当FIFO内元素低于指定值就可以生成中断,中断中给FIFO写入新的数据。还有几个注意

    • 我们只要对SampleRegister进行读操作,FIFO数据就会减少;
    • PWM在被Disable后,FIFO的元素就不再减少了;
    • 如果PWM被进行软复位,FIFO里所有内容会被清除

    回滚和比较事件

    当计数器到PWM_PR寄存器+2的值时会重置到0然后重新开始计数。这个过程和定时中断的计数器一样。这个事件可以当做一次回滚,回滚时输出可以根据设置去置0、1或无反应;这个过程也可以产生一个中断(前提是使能中断),当计数器值累计到sample值,输出状态会按设置进行更改,这是个比较事件,也可以触发一次中断。总之就是信号在到Period的时候反转一下,到Sample值时候再反转一下, 构成一个信号周期。

    寄存器说明

    I.MX6UL提供了8组PWM,每组使用6个寄存器

     其中CNR、PR和SAR是3个16位的寄存器,保存了计数器、Sample和Period的值,后面不在讲了 ,剩下的我们再看看

    PWMCR

    控制寄存器,

    FWM[27:26]:FIFO剩余多少会触发FIFO空中断,一般设置为2,如果1的话比较危险,可能会FIFO空报错

    POUTC[19:18]:输出配置,设置在回滚和比较事件时输出状态

    CLKSRC[17:16]:时钟源,我们一般选择外设时钟源,值为01

    SWR[3]:软复位,设置为1时软复位,复位完成后自动回0

    REPEAT[2:1]:Sample重复,可以设置每个Sample值重复使用的次数

    EN[0]:PWM使能

    PWMSR

    状态寄存器

    FWE[6]:FIFO写错误

    CMP[5]:比较事件

    ROV[4]:回滚事件

    FE[3]:FIFO空状态

    FIFOAV[2:0]:FIFO内元素数量

    中断使能

    中断使能一共有3个bit可以用

    CIE[2]:比较中断使能

    RIE[1]:回滚中断使能

    FIE[0]:FIFO空中断使能,FIFO元素小于FWM指定值触发中断。

    PWMPR

    周期寄存器,计算方法如下

    PWMO:PWM输出信号频率

    PCLK:经过分频器分频后的时钟周期

    period:写入PR寄存器的值

    注意实际周期是period+1,当我们写入PR的值为0xFFFF时实际效果和0xFFFE是一样的。

    代码编写

    代码很简单,主要就是注意定一个全局变量pwm_duty,每次调用设置占空比的时候都会把这个变量修改,这个变量主要作用是给中断服务使用。开始调试时忘了这个变量,一直测不到输出,加了通过不停添加打印节点发现只有duty的值只有4个满足设置要求,应该对应的就是FIFO的深度。肯定是在中断服务有问题,后来发现中断调用函数时传参的值是0,才发现这个问题。

    文件结构

     c文件

    /**
     * @file bsp_pwm.c
     * @author your name (you@domain.com)
     * @brief 
     * @version 0.1
     * @date 2022-01-22
     * 
     * @copyright Copyright (c) 2022
     * 
     */
    #include "bsp_pwm.h"
    #include "stdio.h"
    unsigned char pwm_duty;
    
    /**
     * @brief PWM1初始化
     * 
     * @param period 周期 微秒
     * @param duty   占空比
     */
    void pwm1_init(unsigned int period,unsigned int duty)
    {   
        /**
         * IO初始化 电气性能:
         *bit [16]      : 0 HYS关闭
         *bit [15:14]   : 10 100K上拉
         *bit [13]      : 1 pull功能
         *bit [12]      : 1 pull/keeper使能
         *bit [11]      : 0 关闭开路输出
         *bit [7:6]     : 10 速度100Mhz
         *bit [5:3]     : 010 驱动能力为R0/2
         *bit [0]       : 0 低转换率
         */
        IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_PWM1_OUT,0);
        IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_PWM1_OUT,0xB090);
    
        PWM1->PWMCR = 0;                            //PWMCR清零
        PWM1->PWMCR |= (1<<26)|(1<<16)|(65<<4);     //PWCR[27:26](FWM)=01 [17:16](CLKSRC)=01 [15:4](PRESCALER)=65
    
        pwm1_setperiod(period);                     //设置周期    
    
        for(int i=0;i<4;i++){                       //通过SAM寄存器写入FIFO
            pwm1_setduty(duty);                 //定占空比
        }
    
        PWM1->PWMIR = (1<<0);                       //FIE=1,使能FIFO Empty中断
        system_register_irqHandler(PWM1_IRQn, (system_irq_handler_t)pwm1_irqhandler,NULL);  //中断函数注册
    
        GIC_EnableIRQ(PWM1_IRQn);                   //GIC使能
        PWM1->PWMSR = 0;                            //PWMSR寄存器清零
        PWM1->PWMCR |= (1<<0);                      //PWMCR[0](EN)=1,使能
    
    }
    
    /**
     * @brief 设置周期
     * 
     * @param value 
     */
    void pwm1_setperiod(unsigned int value)
    {
        unsigned int regvalue = 0;
        if (value<2){
            regvalue=2;
        }
        else{
            regvalue=value-2;
        }
        PWM1->PWMPR = (regvalue & 0xFFFF);
    }
    
    /**
     * @brief 设置占空比
     * 
     * @param duty 占空比
     */
    void pwm1_setduty(unsigned char duty)
    {
        unsigned short period;
        unsigned short sample;
        pwm_duty = duty;                //这行代码别忘了,全局变量pwm_duty,中断服务需要用到这个变量
        period = PWM1->PWMPR +2;
        sample = period *duty / 100;
    
        PWM1->PWMSAR = (sample & 0xFFFF);
    }
    
    void pwm1_irqhandler(unsigned int gcciar,void *userParam)
    {
        if(PWM1->PWMSR &(1<<3)){
            pwm1_setduty(pwm_duty);
            PWM1->PWMSR |= (1<<3);
        }
    
    }

    代码很清楚了,备注可以直接看,就是几个寄存器的设置

    头文件

    /**
     * @file bsp_pwm.h
     * @author your name (you@domain.com)
     * @brief 
     * @version 0.1
     * @date 2022-01-22
     * 
     * @copyright Copyright (c) 2022
     * 
     */
    #ifndef __BSP_PWM_H
    #define __BSP_PWM_H
    
    #include "imx6ul.h"
    #include "bsp_int.h"
    
    void pwm1_init(unsigned int period,unsigned int duty);
    void pwm1_setperiod(unsigned int value);
    void pwm1_setduty(unsigned char duty);
    void pwm1_irqhandler(unsigned int gcciar,void *userParam);
    #endif
    bsp_pwm.h

    使用

    模块导入以后,可以在main函数中使用

    int main(void)
    {   
        int_init();
        imx6u_clkinit();
        clk_enable();
        delay_init();
        uart_init();
        key_init();
       
        unsigned char duty = 50;    
        unsigned char kv = 0;
    
        pwm1_init(1000,duty); 
    
        while(1){
        kv = key_getvalue();
        if(kv == KEY0_VALUE){
            duty += 5;
            if(duty>100){
                duty=5;
            }
            pwm1_setduty(duty);
            delay_ms(50);
            printf("duty:%d",duty);
            }
        }
        return 0;
    }

    在main函数中,还使用了按键,每次按键按下时占空比自增5%

    初始化的时候定义的周期是1000,因为用的时钟源是66MHz,分频直接定死的值65对应66分频,周期就是1000微秒,输出频率1KHz很稳定。我用了个手持的示波器测了下输出的值,变化的过程是通过按键改变占空比的过程。这个输出频率我试过250K,很稳定,但是在低频(50Hz

    左右)的时候波形不是特别好,高电平有个下降的趋势,不知道是不是示波器的原因还是怎么的。

  • 相关阅读:
    数据泵使用笔记与相关shell
    手动创建数据库
    归档日志小试
    SQL语句的结果如何反映在SGA与磁盘中
    oracle 表空间
    linux 循环判断、数组、循环
    oracle 连接方式
    Changing Project Binding to Surround SCM Integration Provider with Visual Studio 2010
    On Caching and Evangelizing SQL
    Windows下使用python3 + selenium实现网页自动填表功能
  • 原文地址:https://www.cnblogs.com/yinsedeyinse/p/15834186.html
Copyright © 2020-2023  润新知