在嵌入式系统开发中,定时器是非常重要的外设之一。通过定时器中断,我们可以精确地控制任务的执行时间,比如LED的闪烁频率。本文介绍了如何使用PIC16F1719微控制器的定时器0来控制LED的闪烁。
在编写程序之前,首先需要设置PIC的配置位(Configuration Bits)。配置位用于配置微控制器的系统行为,比如振荡器的类型、看门狗定时器、低电压复位等。在本文的代码中,配置位的设置如下:
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 WRT = OFF /* 禁用写保护 */
#pragma config PPS1WAY = OFF /* 允许多次更改外设引脚选择 */
#pragma config ZCDDIS = ON /* 禁用零交叉检测 */
#pragma config PLLEN = ON /* 启用锁相环(PLL) */
#pragma config STVREN = ON /* 启用栈溢出/下溢复位 */
#pragma config BORV = LO /* 选择低电压复位触发点 */
#pragma config LPBOR = OFF /* 禁用低功耗低电压复位 */
#pragma config LVP = ON /* 启用低电压编程 */
为了使系统时钟达到32MHz,我们使用了内部振荡器(INTOSC)并启用了4倍的PLL(Phase-Locked Loop)。使用下面的代码可以完成系统时钟的配置:
cOSCCON = 0xF0; // 设置INTOSC为8MHz,使用4x PLL,最终系统时钟为32MHz
在这段代码中,我们使用了RD1引脚来控制LED的状态,因此需要将其配置为输出模式:
cTRISDbits.TRISD1 = 0; // 设置RD1为输出
LATDbits.LATD1 = 0; // 初始LED关闭
此外,我们将所有模拟输入引脚设置为数字模式以避免干扰:
cANSELA = 0x00; // 将所有A端口引脚设置为数字模式
ANSELB = 0x00; // 将所有B端口引脚设置为数字模式
ANSELC = 0x00; // 将所有C端口引脚设置为数字模式
定时器0是8位定时器,使用内部时钟并配置为256分频:
cOPTION_REGbits.TMR0CS = 0; // 使用内部时钟
OPTION_REGbits.PSA = 0; // 启用预分频器
OPTION_REGbits.PS0 = 1; // 预分频器设置为256
OPTION_REGbits.PS1 = 1;
OPTION_REGbits.PS2 = 1;
TMR0 = 0; // 初始化定时器0
在这里,定时器溢出后会触发中断,因此我们需要使能定时器0中断:
cINTCONbits.TMR0IE = 1; // 使能定时器0中断
INTCONbits.PEIE = 1; // 使能外设中断
INTCONbits.GIE = 1; // 使能全局中断
在中断服务程序中,我们对计数器进行累加,当计数器达到一定值(比如122次溢出,对应约1秒时间)时,切换LED的状态,并重置计数器:
cvoid __interrupt() timer0_isr(void) {
if (INTCONbits.TMR0IF) {
INTCONbits.TMR0IF = 0; // 清除中断标志
count++; // 增加计数
if (count == 122) { // 当计数器达到122时
LATDbits.LATD1 ^= 1; // 切换LED状态
count = 0; // 重置计数
}
}
}
为了确保系统在启动时稳定工作,我们使用了一个小的延时函数来等待PLL的启动时间:
c__delay_ms(10); // 允许PLL启动时间大约2ms
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端口引脚设置为数字模式
// 配置D1为输出
TRISDbits.TRISD1 = 0;
LATDbits.LATD1 = 0;
// 配置定时器0
OPTION_REGbits.TMR0CS = 0; // 使用内部时钟
OPTION_REGbits.PSA = 0; // 使用预分频器
OPTION_REGbits.PS0 = 1; // 预分频器 256
OPTION_REGbits.PS1 = 1;
OPTION_REGbits.PS2 = 1;
TMR0 = 0; // 定时器0计数器初始化
// 使能定时器中断
INTCONbits.TMR0IE = 1;
INTCONbits.TMR0IF = 0;
INTCONbits.PEIE = 1; // 使能外设中断
INTCONbits.GIE = 1; // 使能全局中断
while (1) {
}
}
void __interrupt() timer0_isr(void) {
// 检查是否是 Timer0 溢出中断
if (INTCONbits.TMR0IF) {
// 重置 Timer0 溢出标志
INTCONbits.TMR0IF = 0;
// 增加计数
count++;
// 1 秒周期
if (count == 122) {
LATDbits.LATD1 ^= 1; // 切换LED状态
count = 0; // 重置计数
}
// 重装载 Timer0 (根据需要,可以添加)
}
}
通过本文的代码,我们可以使用PIC16F1719的定时器0实现精确的时间控制,达到LED每秒闪烁一次的效果。通过定时器中断,避免了轮询的方式,提高了系统的效率和实时性。
本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!