在本篇博客中,我将详细解释如何在PIC16F1719微控制器上使用PWM(脉宽调制)功能,以及通过一段具体的代码演示如何配置和使用PWM来控制输出端口的信号。本文适合对PIC单片机有一定基础知识的读者,希望通过本篇文章能更好地理解PWM的概念和实际应用。
PWM(Pulse Width Modulation,脉宽调制)是一种非常常见的信号调制方式。它通过调节方波的占空比(Duty Cycle)来改变输出的平均电压值。PWM广泛用于电机控制、LED调光、音频信号生成等场景。
占空比是指方波信号在一个周期内高电平持续的时间与整个周期的比值。通过改变占空比,我们可以控制输出设备(如LED的亮度或电机的转速)。
[ 占空比(%) = \frac{高电平时间}{周期时间} \times 100 ]
PIC16F1719是一款Microchip公司的8位单片机,具有CCP(捕获/比较/PWM)模块,支持PWM功能。我们可以通过CCP模块来产生PWM信号,并且可以通过配置相关的定时器(如Timer6)来控制PWM信号的频率。
下面是一个完整的PWM实现代码,该代码通过Timer6定时器和CCP1、CCP2模块生成两个PWM信号,并通过PPS将这些信号映射到引脚RB0和RB1。
c展开代码#pragma config FOSC = INTOSC    // 使用内部时钟
#pragma config WDTE = OFF       // 禁用看门狗定时器
#pragma config PWRTE = OFF      // 禁用上电延时定时器
#pragma config MCLRE = ON       // MCLR引脚功能启用
#pragma config CP = OFF         // 禁用程序存储器代码保护
#pragma config BOREN = OFF      // 禁用低电压复位
#pragma config CLKOUTEN = OFF   // 禁用时钟输出
#pragma config IESO = ON        // 启用内部外部切换模式
#pragma config FCMEN = ON       // 启用故障安全时钟监控
#pragma config LVP = ON         // 启用低电压编程
这些配置位主要用来设置PIC16F1719的工作环境,包括使用内部振荡器、禁用看门狗定时器、启用MCLR引脚等。我们选择使用内部时钟(INTOSC),并配置时钟为32 MHz。
c展开代码// 选择CCP1和CCP2使用Timer6
CCPTMRSbits.C1TSEL = 0b10;
CCPTMRSbits.C2TSEL = 0b10;
// 设置Timer6的预分频为1,PR6=255
T6CONbits.T6CKPS = 0b00;
T6CONbits.TMR6ON = 1;
PR6 = 255;
我们使用了Timer6作为CCP1和CCP2的计时器,预分频系数设置为1,定时器周期寄存器(PR6)设置为255,这样可以生成31.25kHz的PWM频率。
c展开代码// 配置CCP1和CCP2为PWM模式
CCP1CONbits.CCP1M = 0b1100; // CCP1设为PWM模式
CCP2CONbits.CCP2M = 0b1100; // CCP2设为PWM模式
// 设置占空比的最低有效位
CCP1CONbits.DC1B = 00;
CCP2CONbits.DC2B = 00;
CCP1和CCP2的CCP1M和CCP2M位配置为1100,表示开启PWM模式。DC1B和DC2B用于设置占空比的最低有效位,这里默认设置为0。
c展开代码PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 0x00; // 解锁PPS
// 将RB0映射为CCP1的PWM输出
RB0PPSbits.RB0PPS = 0b01100;
// 将RB1映射为CCP2的PWM输出
RB1PPSbits.RB1PPS = 0b01101;
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 0x01; // 锁定PPS
PPS配置用于将PWM信号从CCP1和CCP2映射到I/O引脚RB0和RB1。通过设置RB0PPS和RB1PPS,我们可以将PWM1映射到RB0,PWM2映射到RB1。
c展开代码while (1) {
    // 主循环,执行其他操作
}
主循环中没有任何操作,PWM信号是由硬件自动生成的,我们只需要确保CCP模块和定时器正确配置,剩下的由硬件完成。
在这篇博客中,我们通过一段完整的代码展示了如何在PIC16F1719上使用PWM功能。通过配置定时器、CCP模块以及PPS,我们可以实现灵活的PWM信号输出。PWM广泛应用于电机控制、LED调光等场景,掌握了该功能后,您可以在实际项目中更好地控制外部设备。
c展开代码/* PIC16F1719 配置位设置 */
#pragma config FOSC = INTOSC    /* 振荡器选择位 (INTOSC 振荡器: CLKIN 引脚上的 I/O 功能) */
#pragma config WDTE = OFF       /* 看门狗定时器使能 (WDT 禁用) */
#pragma config PWRTE = OFF      /* 上电定时器使能 (PWRT 禁用) */
#pragma config MCLRE = ON       /* MCLR 引脚功能选择 (MCLR/VPP 引脚功能为 MCLR) */
#pragma config CP = OFF         /* 闪存程序存储器代码保护 (程序存储器代码保护禁用) */
#pragma config BOREN = OFF      /* 低电压复位使能 (低电压复位禁用) */
#pragma config CLKOUTEN = OFF   /* 时钟输出使能 (CLKOUT 功能禁用。CLKOUT 引脚上的 I/O 或振荡器功能) */
#pragma config IESO = ON        /* 内外部切换模式 (内外部切换模式使能) */
#pragma config FCMEN = ON       /* 故障安全时钟监控器使能 (故障安全时钟监控器使能) */
#pragma config WRT = OFF        /* 闪存自写保护 (写保护关闭) */
#pragma config PPS1WAY = OFF    /* 外设引脚选择单向控制 (PPSLOCK 位可以通过软件反复设置和清除) */
#pragma config ZCDDIS = ON      /* 零交叉检测禁用 (ZCD 禁用。ZCD 可以通过设置 ZCDCON 的 ZCDSEN 位来启用) */
#pragma config PLLEN = ON       /* 锁相环使能 (4x PLL 使能) */
#pragma config STVREN = ON      /* 栈溢出/下溢复位使能 (栈溢出或下溢将导致复位) */
#pragma config BORV = LO        /* 低电压复位电压选择 (低电压复位电压 (Vbor),选择低触发点。) */
#pragma config LPBOR = OFF      /* 低功率低电压复位 (低功率低电压复位禁用) */
#pragma config LVP = ON         /* 低电压编程使能 (低电压编程使能) */
/* 定义系统振荡器频率 */
#define _XTAL_FREQ (32000000ul)
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//counter variable
int count = 0;
void main(void) {
    unsigned char index = 0;
    /* 程序初始化设置 */
    OSCCON = 0xF0;                      /* 设置 INTOSC 为 8MHz,并使用 4xPLL,使系统振荡器达到 32MHz */
    INTCON = 0;                         /* 禁用所有中断源 */
    PIE1 = 0;
    PIE2 = 0;
    PIE3 = 0;
    // Allow PLL startup time ~2 ms
    __delay_ms(10);
    ANSELA = 0x00;  // 将所有A端口引脚设置为数字模式
    ANSELB = 0x00;  // 将所有B端口引脚设置为数字模式
    ANSELC = 0x00;  // 将所有C端口引脚设置为数字模式
    
    
    
    
    
    ////////////////////
    // Configure Ports
    ///////////////////
    TRISDbits.TRISD1 = 0;
    
    // Set PIN B0 as output
    TRISBbits.TRISB0 = 0;
    
    // Set PIN B1 as output
    TRISBbits.TRISB1 = 0;
    
    // Turn off analog
    ANSELB = 0;
    
    /////////////////////
    // Configure Timer6
    /////////////////////
    
    // Select PWM timer as Timer6 for CCP1 and CCP2
    CCPTMRSbits.C1TSEL = 0b10;
    CCPTMRSbits.C2TSEL = 0b10;
    
 
    // Enable timer Increments every 125 ns (32 MHz clock) 1000/(32/4)
    // Period = 256 x 0.125 us = 31.25 us
    
    //                          Crystal Frequency 
    //    PWM Freq  = ----------------------------------------- 
    //                  (PRX + 1) * (TimerX Prescaler) * 4
    
    //    PWM Frequency = 32 000 000 / 256 * 1 * 4
    //    PWM Frequency = 31.250 kHz
    
    // Prescale = 1
    T6CONbits.T6CKPS = 0b00;
    
    // Enable Timer6
    T6CONbits.TMR6ON = 1;
    
    // Set timer period
    PR6 = 255;
    
    
    //////////////////////////
    // Configure PWM
    /////////////////////////
    
    // Configure CCP1
     
    // LSB's of PWM duty cycle = 00
    CCP1CONbits.DC1B = 00;
    
    // Select PWM mode
    CCP1CONbits.CCP1M = 0b1100;
    
    // Configure CCP2
    
    // LSB's of PWM duty cycle = 00
    CCP2CONbits.DC2B = 00;
    
    // Select PWM mode
    CCP2CONbits.CCP2M = 0b1100;
    
    
    //////////////////////////////
    // Configure PPS
    /////////////////////////////
    
    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0x00; // unlock PPS
    
    // Set RB0 to PWM1
    RB0PPSbits.RB0PPS = 0b01100;
    
    // Set RB1 to PWM2
    RB1PPSbits.RB1PPS = 0b01101;
    
    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0x01; // lock PPS
    
    
    // enable peripheral interrupt
    INTCONbits.PEIE = 1;
    INTCONbits.GIE = 1;         // 使能全局中断
    while (1) {
        // Main loop does nothing, all action in ISR
    }
}
希望这篇文章能够帮助您更好地理解PWM在PIC单片机上的实现。


本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!