1

美好的一天,我正在使用 PIC24FJ64GA002 微控制器,并且正在努力让 PWM 为我想要使用的伺服系统工作。我的代码如下:

// PIC24FJ64GA002 Configuration Bit Settings

// 'C' source line config statements

// CONFIG2
#pragma config POSCMOD = NONE           // Primary Oscillator Select (Primary oscillator disabled)
#pragma config I2C1SEL = PRI            // I2C1 Pin Location Select (Use default SCL1/SDA1 pins)
#pragma config IOL1WAY = ON             // IOLOCK Protection (Once IOLOCK is set, cannot be changed)
#pragma config OSCIOFNC = OFF           // Primary Oscillator Output Function (OSC2/CLKO/RC15 functions as CLKO (FOSC/2))
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor (Clock switching and Fail-Safe Clock Monitor are disabled)
#pragma config FNOSC = FRC              // Oscillator Select (Fast RC Oscillator (FRC))
#pragma config SOSCSEL = SOSC           // Sec Oscillator Select (Default Secondary Oscillator (SOSC))
#pragma config WUTSEL = LEG             // Wake-up timer Select (Legacy Wake-up Timer)
#pragma config IESO = ON                // Internal External Switch Over Mode (IESO mode (Two-Speed Start-up) enabled)

// CONFIG1
#pragma config WDTPS = PS32768          // Watchdog Timer Postscaler (1:32,768)
#pragma config FWPSA = PR128            // WDT Prescaler (Prescaler ratio of 1:128)
#pragma config WINDIS = ON              // Watchdog Timer Window (Standard Watchdog Timer enabled,(Windowed-mode is disabled))
#pragma config FWDTEN = ON              // Watchdog Timer Enable (Watchdog Timer is enabled)
#pragma config ICS = PGx1               // Comm Channel Select (Emulator EMUC1/EMUD1 pins are shared with PGC1/PGD1)
#pragma config GWRP = OFF               // General Code Segment Write Protect (Writes to program memory are allowed)
#pragma config GCP = OFF                // General Code Segment Code Protect (Code protection is disabled)
#pragma config JTAGEN = OFF             // JTAG Port Enable (JTAG port is disabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

/*
 * File:   34660046LAB2.c
 * Author: leone
 *
 * Created on 06 September 2021, 1:27 PM
 */


   

 

   

 #include "p24FJ64GA002.h"

        #include "xc.h"

        #define _LATR15 OC1R
        
        
        
        
        int main(void) {
        
            
            T2CON = 0x8010;
            TMR2=0;
            PR2=9999;
            
            _T2IP=4;  //Default priority value
            _T2IF=0;  //Clears interrupt flag before interrupt
            _T2IE=1;  //Enables interrupt
            
            OC1CONbits.OC  = 0;        // Output compare channel is disabled
        OC1R           = 0x1388 ; // Initialize Compare Register1 with 50% duty cycle
        
        OC1CONbits.OCSIDL   = 0;     // Output capture will continue to operate in CPU Idle mode
        OC1CONbits.OCFLT    = 0;     // No PWM Fault condition has occurred (this bit is only used when OCM<2:0> = 111)
        OC1CONbits.OCTSEL   = 0;     // Timer2 is the clock source for output Compare
        OC1CONbits.OCM      = 0x6;   // PWM mode on OC, Fault pin disabled
            
            
            
            TRISBbits.TRISB15=0;
           _LATB15=0;
            while(1)
            {
                if(TMR2>OC1R)
                {
                    _LATB15=1;
                }
                else
                {
                    _LATB15=0;
                }
            }
            return 0;
        }

我将 T1CON 的周期设置为 2 ms,而 OC1R 的周期为该周期的一半,这将导致占空比为 50%。我正在使用 FRC 振荡器 (8MHz),我的预分频器值为 <0,1>。我知道是 OC1R 引脚给出了周期为高的周期,但在数据表中,他们将其称为引脚,但是他们没有给出它是什么引脚(即 A0、A1、R15 等)。我也可以找到很少的示例代码来解释如何很好地编码。如果有人是某种帮助的PIC专家,将不胜感激!MCU 的数据表可在https://www.microchip.com/en-us/product/PIC24FJ64GA002下载。

4

2 回答 2

1

这是一个使用 MPLABX v5.50 和 XC16 v1.70 构建的完整应用程序:

/*
 * File:   main.c
 * Author: dan1138
 * Target: PIC24FJ64GA002
 * Compiler: XC16 v1.70
 * IDE: MPLABX v5.50
 *
 * Created on October 8, 2021, 1:12 PM
 *
 *                                 PIC24FJ64GA002
 *                  +-------------------:_:-------------------+
 *      ICD_VPP --> :  1 MCLR                         VDD  28 : <-- 3v3
 *              < > :  2 RA0/AN0                      VSS  27 : <-- GND
 *              < > :  3 RA1/AN1             AN9/RP15/RB15 26 : < > 
 *      ICD_PGD < > :  4 RB0/PGD1/AN2        AN6/RP14/RB14 25 : < > 
 *      ICD_PGC < > :  5 RB1/PGC1/AN3        AN7/RP13/RB13 24 : < > 
 *      PWM/OC1 < > :  6 RB2/RP2/SDA2/AN4    AN8/RP12/RB12 23 : < > 
 *              < > :  7 RB3/RP3/SCL2/AN5        RP11/RB11 22 : <*> 
 *              - > :  8 VSS                     RP10/RB10 21 : <*> 
 *              < > :  9 RA2/OSCI                     VCAP 20 : <-- 10uF
 *              < > : 10 RA3/OSCO                     VSS  19 : <-- GND
 *              < > : 11 RB4/RP4             SDA1/RP9/RB9  18 : <*> 
 *              < > : 12 RA4                 SCL1/RP8/RB8  17 : <*> 
 *          3v3 --> : 13 VDD                      RP7/RB7  16 : <*>
 *              <*> : 14 RB5/RP5/PGD3        PGC3/RP6/RB6  15 : <*> 
 *                  +-----------------------------------------+
 *                                    DIP-28
 *               * Indicates 5.0 volt tolerant input pins.
 *
 * Description:
 *  Initialize the controller to use a system oscillator of 8MHz from the on chip Fast RC oscillator.
 *  Setup the OC1 function to provide a 4KHz square wave output on GPIO pin RB2.
 * 
 * Notes:
 * See: https://stackoverflow.com/questions/69453833/pwm-settings-in-pic24fj64ga002
 * 
 */

#pragma config POSCMOD = NONE, I2C1SEL = PRI, IOL1WAY = OFF, OSCIOFNC = ON
#pragma config FCKSM = CSECMD, FNOSC = FRC, SOSCSEL = SOSC, WUTSEL = LEG
#pragma config IESO = ON, WDTPS = PS32768, FWPSA = PR128, WINDIS = ON
#pragma config FWDTEN = OFF, ICS = PGx1, GWRP = OFF, GCP = OFF, JTAGEN = OFF

#include "xc.h"

/*
 * Define the system oscillator frequency that this code will setup
 */
#define FSYS (8000000ul)
#define FCY  (FSYS/2ul)

/*
 * Initialize this PIC
 */
void PIC_Init(void) {   
    /* Disable all interrupt sources  */ 
    __builtin_disi(0x3FFF); /* disable interrupts for 16383 cycles */
    IEC0 = 0;
    IEC1 = 0;
    IEC2 = 0;
    IEC3 = 0;
    IEC4 = 0;
    __builtin_disi(0x0000); /* enable interrupts */

    CLKDIV =  0x0000; /* set for 8MHz FRC clock operations */
    AD1PCFG = 0xffff; /* Set for digital I/O */
    CMCON   = 0x0000;

    _NSTDIS = 1;    /* disable interrupt nesting */
    
    TRISA   = 0xFFFF;
    TRISB   = 0xFFFF;
}
/*
 * Initialize OC1 for PWM operation on PORTB bit RB2/RP2
 */
void OC1_Init(void) {
    OC1CON = 0;             /* turn off OC1 */
    AD1PCFGbits.PCFG4 = 1;  /* make GPIO RB2/RP2/AN4 a digital I/O pin */
    LATBbits.LATB2 = 0;     /* make GPIO RB2/RP2/AN4 output low */
    TRISBbits.TRISB2 = 0;   /* make GPIO RB2/RP2/AN4 a digital output */
    _RP2R = 18;             /* magic number to assign OC1 to RB2, see DS39881E-page 109 */
    OC1CONbits.OCTSEL = 0;  /* Use TIMER2 clock input and prescaler settings as clock for PWM count register */
    OC1CONbits.OCM = 0b110; /* Set OC1 for PWM mode, fault shutdown disabled */
    T2CON = 0;              /* turn off TIMER2 */
    T2CONbits.TCKPS = 0b00; /* set TIMER2 prescale as 1:1 */
    T2CONbits.TCS = 0;      /* set TIMER2 clock source as FSYS/2 */
    PR2 = (FCY/4000ul)-1;   /* set PWM period to 4KHz */
    OC1RS = (PR2+1)>>1;     /* set PWM duty cycle to 50% */
    T2CONbits.TON = 1;      /* turn on TIMER2 */
}
/*
 * main application
 */
int main(void) {
    
    PIC_Init();
    OC1_Init();
    /*
     * application process loop
     */
    for(;;) {
        /* poll for start of PWM period */
        if(IFS0bits.T2IF){
            IFS0bits.T2IF= 0; /* put a breakpoint here to verify PWM timing with simulator stop watch */
            Nop();
            Nop();
            Nop();
        }
    }
    return 0;
}

该代码在 MPLABX 仿真工具中可以正确运行。这几乎从来都不是真的。恕我直言,MPLABX 模拟器是垃圾。

这是模拟会话工作的屏幕截图: 在此处输入图像描述

关于 PIC24FJ64GA002 输出比较模块的一个重要说明是,它是早期的硅实现,较新的控制器为每个输出比较模块具有独立的周期计数寄存器。这意味着输出比较模块周期与这些控制器上的 TIMER 周期无关。我提到这个是因为它真的让我很困惑。

于 2021-10-08T20:42:14.140 回答
1

首先为 PWM 输出配置 IO 引脚,我假设您使用 SPDIP 封装并想使用 OC1 PWM 输出引脚:

// Unlock Registers
__builtin_write_OSCCONL(OSCCON & 0xBF);
// Configure Output Functions (Table 10-3)
// Assign OC1 To Pin RP2
RPOR1bits.RP2R = 18;
// Lock Registers
__builtin_write_OSCCONL(OSCCON | 0x40);

有关输出引脚配置,请参见数据表的10.4.3.2 输出映射部分。

按照数据表的14.3 脉冲宽度调制模式部分中的步骤:

  1. 通过写入选定的定时器周期寄存器 (PRy) 来设置 PWM 周期。如果您的周期为 2 ms,则计算 PRy 寄存器值:
#define Fosc          ( 8000000 )
#define PWM_PERIOD_MS ( 2 )
#define PWM_FREQ_HZ   ( 1000 / PWM_PERIOD_MS ) // In this case 500Hz
#define PRy_VALUE     (uint16_t) ( (( Fosc ) / ( 4 * TMRyPS * PWM_FREQ)) - 1 )

// Somewhere in the init func or code assign the computed value for the period register
PRx = PRx_VALUE;
  1. 通过写入 OCxRS 寄存器来设置 PWM 占空比。由于您需要 50% 的占空比,请计算 OCxRS 寄存器的值:
#define DUTY          (50)
#define DUTY_VALUE    (uint16_t) ( ( 4 * ( TMRyPS - 1 ) * DUTY ) / 100 )

// Somewhere in the init func or code assign the computed value for the duty cycle register
OCxRS = DUTY_VALUE;
  1. 使用初始占空比写入 OCxR 寄存器。DUTY_VALUE在 OCxRS 分配之后立即将 分配给 OCxR 寄存器:
    OCxR = DUTY_VALUE;

  2. 如果需要,为定时器和输出比较模块启用中断。PWM 故障引脚使用需要输出比较中断。

  3. 通过写入输出比较模式位 OCM<2:0> (OCxCON<2:0>)将输出比较模块配置为两种 PWM 操作模式之一。

 OC1CONbits.OCTSEL   = 0;     // Timer2 is the clock source for output Compare
 OC1CONbits.OCM      = 0x6;   // PWM mode on OC, Fault pin disabled
  1. 通过设置 TON(TyCON<15>)= 1 设置 TMRy 预分频值并启用时基。
T2CON = 0x0010 // Timer2 prescaler 1:8, internal clock
T2CON.TON = 1;

从现在开始,如果上述步骤正确完成,您应该可以使 PWM 正常工作。按照此处的说明修改您的代码。然后试一试,让我知道结果。

于 2021-10-06T20:52:11.933 回答