dsPIC系列-1:dsPIC33点灯 [I/O、RCC、定时器]

文章目录


前言

dsPIC系列教程少之又少,因工作接触到dsPIC借此机会记录一下学习过程。

本例采用芯片dsPIC33FJ128GP804


一、I/O端口配置(GPIO)

本例以RC9引脚为例


1.TRISx 寄存器

TRISx 寄存器设置输入or输出引脚

配置 方向
0 输出
1 输入

2.PORTx 寄存器

通过PORTx寄存器访问I/O引脚上的数据。读PORTx寄存器是读取I/O引脚上的值,而写PORTx寄存器是将值写入端口数据锁存器。

3.LATx 寄存器

与 I/O 引脚相关的 LATx 寄存器消除了可能在执行读 - 修改 - 写指令时发生的问题。

写 LATx 寄存器就是将数据值写入端口锁存器。

读 LATx 寄存器就是读取保存在端口锁存器中的数据值。

4.代码

c 复制代码
void GPIO_Init(void)
{
    // 配置RC9为数字输出高电平
    TRISCbits.TRISC9 = 0;   // 设置RC9为输出模式 (0 = 输出)
    LATCbits.LATC9 = 1;     // 设置RC9初始输出为高电平 (1 = 高)
}

二、系统时钟配置(RCC)

1.时钟树

本例选择外部陶瓷晶振(24MHz)

主振荡器经过PLL将分给系统频率FOSC,指令周期时钟表示为 FCY,程序计数器时钟FPC

2.时钟分频

本例外部晶振为24MHz,因此运行在HS模式

本例芯片最大指令周期为40MIPS

因此设定N1=3, M=20, N2=2

同时满足以下条件

F_IN = 24MHz / 3 = 8.0 MHz

F_VCO = 8.0MHz * 20 = 160 MHz

Fosc = 160MHz / 2 = 80 MHz

Fcy = 80MHz / 2 = 40 MHz (40 MIPS达成)

配置举例:

例程代码:

c 复制代码
// Select Internal FRC at POR
_FOSCSEL(FNOSC_FRC & IESO_OFF);
// Enable Clock Switching and Configure POSC in HS mode
_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_HS);
int main()
{
// Configure PLL prescaler, PLL postscaler, PLL divisor
PLLFBD=18; // M=20
CLKDIVbits.PLLPOST=0; // N2=2
CLKDIVbits.PLLPRE=1; // N1=3
// Initiate Clock Switch to Primary Oscillator with PLL (NOSC=0b011)
__builtin_write_OSCCONH(0x03);
__builtin_write_OSCCONL(OSCCON | 0x01);
// Wait for Clock switch to occur
while (OSCCONbits.COSC!= 0b011);
// Wait for PLL to lock
while (OSCCONbits.LOCK!= 1);

三、定时器1配置(Timer1)

本例选择FCY内部时钟源

1.T1CON:Timer1 控制寄存器

详细介绍看这里

2.TMR1:Timer1 寄存器

实时计数

3.PR1:Timer1 周期寄存器

设置周期

4.实例

本例设定定时器1为1ms中断作为系统时基

FCY = 40MHz

想要1ms中断 FTIMER1 = FCY / TCKPS / (PR1+1)

c 复制代码
volatile uint32_t systemTickCount = 0; // 系统时基计数器

void TMR1_Initialize(void) {
    T1CON = 0x0; //清空寄存器
    TMR1 = 0; // 清空计数器
    
    T1CONbits.TON = 0; // 先关闭定时器
    T1CONbits.TCS = 0; // 选择内部指令周期时钟(F_cy)
    T1CONbits.TGATE = 0; // 禁止门控模式
    T1CONbits.TCKPS = 0b01; // 设置预分频比 1:8 (根据计算调整)
    
    PR1 = 4999; // 设置周期寄存器值 (根据计算调整)

    IFS0bits.T1IF = 0; // 清除定时器1中断标志
    IEC0bits.T1IE = 1; // 使能定时器1中断
    IPC0bits.T1IP = 4; // 设置中断优先级(1-7),根据系统需要设定

    T1CONbits.TON = 1; // 启动定时器
}

void __attribute__((interrupt, auto_psv)) _T1Interrupt(void) {
    IFS0bits.T1IF = 0; // 必须手动清除中断标志
    systemTickCount++; // 系统时基加1,每1ms加一次
}

4.点灯完整代码

c 复制代码
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>


void GPIO_Init(void);
void TMR1_Initialize(void);
volatile uint32_t systemTickCount = 0; // 系统时基计数器
#define _XTAL_FREQ 80000000UL // 定义为80MHz,指示Fosc

// Select Internal FRC at POR
_FOSCSEL(FNOSC_FRC & IESO_OFF);
// Enable Clock Switching and Configure POSC in HS mode
_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_HS);

int main(void) {
    uint32_t u32LEDLastTime;
    OSC_Init_Safe_40MIPS();
    GPIO_Init();
    TMR1_Initialize();
    // 全局中断使能
    INTCON1bits.NSTDIS = 0; // 允许中断嵌套(根据需求)
    __builtin_enable_interrupts(); // 使能全局中断
    u32LEDLastTime = systemTickCount;
    while(1) {
        if(systemTickCount - u32LEDLastTime> 1000)
        {
            LATCbits.LATC9 = ~LATCbits.LATC9; // 翻转RC9电平
            u32LEDLastTime = systemTickCount;
        }
    }
    return 0;
}

void GPIO_Init(void)
{
    // 配置RC9为数字输出高电平
    TRISCbits.TRISC9 = 0;   // 设置RC9为输出模式 (0 = 输出)
    LATCbits.LATC9 = 1;     // 设置RC9初始输出为高电平 (1 = 高)
}

void TMR1_Initialize(void) {
    T1CON = 0x0; //清空寄存器
    TMR1 = 0; // 清空计数器
    
    T1CONbits.TON = 0; // 先关闭定时器
    T1CONbits.TCS = 0; // 选择内部指令周期时钟(F_cy)
    T1CONbits.TGATE = 0; // 禁止门控模式
    T1CONbits.TCKPS = 0b01; // 设置预分频比 1:8 (根据计算调整)
    
    PR1 = 4999; // 设置周期寄存器值 (根据计算调整)

    IFS0bits.T1IF = 0; // 清除定时器1中断标志
    IEC0bits.T1IE = 1; // 使能定时器1中断
    IPC0bits.T1IP = 4; // 设置中断优先级(1-7),根据系统需要设定

    T1CONbits.TON = 1; // 启动定时器
}

void OSC_Init_Safe_40MIPS(void){
    // Configure PLL prescaler, PLL postscaler, PLL divisor
    PLLFBD=18; // M=48
    CLKDIVbits.PLLPOST=0; // N2=2
    CLKDIVbits.PLLPRE=1; // N1=2
    // Initiate Clock Switch to Primary Oscillator with PLL (NOSC=0b011)
    __builtin_write_OSCCONH(0x03);
    __builtin_write_OSCCONL(OSCCON | 0x01);
    // Wait for Clock switch to occur
    while (OSCCONbits.COSC!= 0b011);
    // Wait for PLL to lock
    while (OSCCONbits.LOCK!= 1);
}


void __attribute__((interrupt, auto_psv)) _T1Interrupt(void) {
    IFS0bits.T1IF = 0; // 必须手动清除中断标志
    systemTickCount++; // 系统时基加1,每1ms加一次
}

总结

本例实现LED间隔1s闪烁功能,涉及IO配置,RCC时钟配置,定时器配置。(本人学习记录,切勿转载)

相关推荐
CQ_YM2 小时前
51单片机(1)
单片机·嵌入式硬件·51单片机
qq_401700412 小时前
单片机之ADC(模拟数字转换器)
单片机·嵌入式硬件
无事好时节2 小时前
51 单片机GPIO / 按键 / 中断 / 定时器 / PWM
单片机·嵌入式硬件
一枝小雨4 小时前
【OTA专题】17 打通Bootloader与App逻辑之间的通信
stm32·单片机·嵌入式·流程图·freertos·ota·bootloader
2401_863318636 小时前
基于单片机的家庭防盗报警系统
单片机·嵌入式硬件
一枝小雨7 小时前
【OTA专题】18 OTA性能优化:优化bootloader存储空间与固件完整性校验(CRC)
stm32·单片机·性能优化·嵌入式·freertos·ota·bootloader
iYun在学C7 小时前
驱动程序(注册字符设备)
linux·嵌入式硬件
尼喃7 小时前
PW2605Z,专为系统安全护航的高可靠性负载开关
stm32·单片机·嵌入式硬件
阿昊真人7 小时前
stm32 按键中断
stm32·单片机·嵌入式硬件
代码游侠7 小时前
学习笔记——51单片机学习
笔记·stm32·单片机·嵌入式硬件·51单片机