文章目录
- 前言
- 一、I/O端口配置(GPIO)
-
- [1.TRISx 寄存器](#1.TRISx 寄存器)
- [2.PORTx 寄存器](#2.PORTx 寄存器)
- [3.LATx 寄存器](#3.LATx 寄存器)
- 4.代码
- 二、系统时钟配置(RCC)
- 三、定时器1配置(Timer1)
-
- [1.T1CON:Timer1 控制寄存器](#1.T1CON:Timer1 控制寄存器)
- [2.TMR1:Timer1 寄存器](#2.TMR1:Timer1 寄存器)
- [3.PR1:Timer1 周期寄存器](#3.PR1:Timer1 周期寄存器)
- 4.实例
- 4.点灯完整代码
- 总结
前言
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时钟配置,定时器配置。(本人学习记录,切勿转载)