第 1 章 PIC16F877 的外围功能模块 1.1.2 简单应用实例该例用于令与 PORTD 口相连的 8 个发光二极管前 4 个点亮, 后 4 个熄灭 在调试程序前, 应使与 PORTD 口相连的 8 位拔码开关拔向相应的位置 例 1.1 PORTD 输出 #include <pic.h> main() TRISD=0X00 /*TRISD 寄存器被赋值,PORTD 每一位都为输出 */ /* 循环执行点亮发光二极管的语句 */ PORTD=0XF0 /* 向 PORTD 送数据, 点亮 LED( 由实验模板 */ /* 的设计决定相应位置低时 LED 点亮 ) */ 1.2.1 MSSP 模块 SPI 方式功能简介 下面是一段简单的 SPI 初始化例程, 用于利用 SPI 工作方式输出数据的场合 例 1.2 SPI 初始化程序 /*spi 初始化子程序 */ void SPIINIT() PIR1=0 /* 清除 SPI 中断标志 */ SSPCON=0x30 /* SSPEN=1CKP=0, FOSC/4 */ SSPSTAT=0xC0 TRISC=0x00 /*SDO 引脚为输出,SCK 引脚为输出 */ 1.2.3 程序清单 下面给出已经在实验板上调试通过的一个程序, 可作为用户编制其它程序的参考 #include <pic1687x.h> /* 该程序用于在 8 个 LED 上依次显示 1~8 等 8 个字符 */ static volatile int table[20]=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,0x80, 0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0x7f,0xbf,0x89,0xff volatile unsigned char data #define PORTAIT(adr,bit) ((unsigned)(&adr)*8+(bit)) /* 绝对寻址位操作指令 */ 88
staticbit PORTA_5 @ PORTAIT(PORTA,5) /*spi 初始化子程序 */ void SPIINIT() PIR1=0 SSPCON=0x30 /* SSPEN=1CKP=0, FOSC/4 */ SSPSTAT=0xC0 TRISC=0x00 /*SDO 引脚为输出,SCK 引脚为输出 */ /* 系统各输入输出口初始化子程序 */ void initial() TRISA=0x00 /*A 口设置为输出 */ INTCON=0x00 /* 关闭所有中断 */ PORTA_5=0 /*LACK 送低电平, 为锁存做准备 */ /*SPI 发送子程序 */ void SPILED(int data) SSPBUF=data /* 启动发送 */ do while(sspif==0) /* 等待发送完毕 */ SSPIF=0 /* 清除 SSPIF 标志 */ /* 主程序 */ main() unsigned I initial() /* 系统初始化 */ SPIINIT() /*SPI 初始化 */ for(i=8i>0i--) /* 连续发送 8 个数据 */ data=table[i] /* 通过数组的转换获得待显示的段码 */ SPILED(data) /* 发送显示段码显示 */ PORTA_5=1 /* 最后给锁存信号, 代表显示任务完成 */ 89
1.3.3 程序清单 下面给出已经在实验板上调试通过的程序, 可作为用户编制其它程序的参考 有关显 示部分的 SPI 初始化, 请读者参考 1.2 节 #include <pic.h> /* 该程序用于按下相应的键时, 在第一个 8 段 LED 上显示相应的 1~4 的字符 */ #define PORTAIT(adr,bit) ((unsigned)(&adr)*8+(bit)) /* 绝对寻址位操作指令 */ static bit PORTA_5 @ PORTAIT(PORTA,5) #define PORTBIT(adr, bit) ((unsigned)(&adr)*8+(bit)) /* 绝对寻址位操作指令 */ static bit PORTB_5 @ PORTBIT(PORTB,5) static bit PORTB_4 @ PORTBIT(PORTB,4) static bit PORTB_1 @ PORTBIT(PORTB,1) static bit PORTB_2 @ PORTBIT(PORTB,2) unsigned int I unsigned char j int data /*spi 初始化子程序 */ void SPIINIT() PIR1=0 SSPCON=0x30 SSPSTAT=0xC0 TRISC=0xD7 /*SDO 引脚为输出,SCK 引脚为输出 */ /* 系统各输入输出口初始化子程序 */ void initial() TRISA=0xDF TRISB=0XF0 /* 设置与键盘有关的各口的数据方向 */ INTCON=0x00 /* 关闭所有中断 */ data=0x00 /* 待显示的寄存器赋初值 */ PORTB=0X00 /*RB1 RB2 先送低电平 */ j=0 /* 软件延时子程序 */ void DELAY() 90
for(i = 6553 --i ) continue /* 键扫描子程序 */ int KEYSCAN() if ((PORTB_5==0) (PORTB_4==0)) break /* 等待有键按下 */ DELAY() /* 软件延时 */ if ((PORTB_5==0) (PORTB_4==0)) KEYSERVE() /* 如果仍有键按下, 则调用键服务子程序 */ else j=0x00 /* 如果为干扰, 则令返回值为 0*/ return(j) /* 键服务子程序 */ int KEYSERVE() PORTB=0XFD if(portb_5==0) j=0x01 if(portb_4==0) j=0x03 PORTB=0XFB if(portb_5==0) j=0x02 if(portb_4==0) j=0x04/* 以上根据按下的键确定相应的键值 */ PORTB=0X00 /* 恢复 PORTB 的值 */ if((portb_5==1)&&(portb_4==1)) break/* 等待键盘松开 */ return(j) /*SPI 发送子程序 */ void SPILED(int data) SSPBUF=data /* 启动发送 */ do 91
while(sspif==0) /* 等待发送完毕 SSPIF=0 /* 主程序 */ main() static int table[20]=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,0x80,0x90, 0x88,0x83,0xc6,0xa1,0x86,0x8e,0x7f,0xbf,0x89,0xff initial()/* 系统初始化 */ SPIINIT()/*SPI 初始化 */ KEYSCAN() if(j!=0) /* 如果 j=0, 证明先前的按键为干扰, 则不予显示 */ data=table[j] PORTA_5=0 /*LACK 信号清 0, 为锁存做准备 */ SPILED(data) PORTA_5=1 /* 最后给锁存信号, 代表显示任务完成 */ 1.4.1 PORTB 端口 电平变化中断 简介 例 1.3 PORTB 口 电平变化中断 初始化子程序 /*B 口 电平变化中断 初始化子程序 */ void PORTBINT( ) TRISB=0XF0 /* 设置相应口的输入输出方式 */ OPTION=0x7F /*B 口弱上拉有效 */ PORTB=0X00 /*RB1,RB2 先送低电平 */ RBIE=1 /*B 口变位中断允许 */ PORTB=PORTB /* 读 B 口的值, 以锁存旧值, 为变位中断创造条件 */ 1.4.3 程序清单 下面给出一个调试通过的例程, 以供读者参考 有关显示的部分请读者参考前面章节 92
该程序中寄存器的位都用头文件中定义的位, 如 RB5 表示 PORTB 的第 5 位, 而不像前面几节那样自己定义 #include <pic.h> /* 该程序用于通过 PORTB 的 " 电平变化中断 " 进行键盘的识别 */ /* 程序设置一个键值寄存器 j, 当按下 S9 键时 j=1, 按下 S11 键时 */ /*j=2, 按下 S10 键时,j=3, 按下 S12 键时 j=4*/ unsigned char data unsigned int I unsigned char j const char table[20]=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,0x80,0x90, 0x88,0x83,0xc6,0xa1,0x86,0x8e,0x7f,0xbf,0x89,0xff /*B 口 电平变化中断 初始化子程序 */ void PORTBINT() TRISB=0XF0 /* 设置相应口的输入输出方式 */ OPTION=0x7F PORTB=0X00 /*RB1, RB2 先送低电平 */ RBIE=1 /*B 口变位中断允许 */ PORTB=PORTB /* 读 B 口的值, 为变位中断创造条件 */ /*spi 初始化子程序 */ void PIR1=0 SPIINIT() SSPCON=0x30 SSPSTAT=0xC0 TRISC=0xD7 /*SDO 引脚为输出,SCK 引脚为输出 */ /* 系统各输入输出口初始化子程序 */ void initial() TRISA=0xDF INTCON=0x00 /* 关闭所有中断 */ data=0x00 /* 待显示的寄存器赋初值 */ /* 键服务子程序 */ void KEYSERVE() 93
PORTB=0XFD if(rb5==0) j=0x01 if(rb4==0) j=0x03 PORTB=0XFB if(rb5==0) j=0x02 if(rb4==0) j=0x04 /* 以上通过逐行逐列扫描, 以确定是何键按下 */ PORTB=0X00 /* 恢复 PORTB 的值 */ /* 软件延时子程序 */ void DELAY() for(i = 6553 --i ) continue /*SPI 发送子程序 */ void SPILED(int data) SSPBUF=data /* 启动发送 */ do while(sspif==0) SSPIF=0 void IDEDIS() KEYSERVE() /* 进行键盘的识别 */ data=table[j] /* 获得需要送出显示的段码 */ RA5=0 /*LACK 信号清 0, 为锁存做准备 */ SPILED(data) RA5=1 /* 最后给一个锁存信号, 代表显示任务完成 */ /* 中断服务程序 */ void interrupt keyint(void) DELAY() /* 软件延时 */ if ((RB5==0) (RB4==0)) /* 该语句除了能够确认按键是否为干扰外,*/ /* 还可以屏蔽一次键松开时引起的中断 */ 94
IDEDIS() /* 键识别和显示模块 */ PORTB=PORTB /* 读 B 口的值, 改变中断发生的条件, 避免键 */ /* 一直按下时, 连续进行键识别 */ RBIF=0 /* 键扫描时可能会产生 " 电平变化 " 而使 RBIF*/ /* 置 1, 再清除一次 RBIF 以避免额外中断 */ main() initial() /* 系统初始化 */ PORTBINT() /*B 口变位中断初始化 */ SPIINIT() /* 利用 SPI 显示初始化 */ ei() /* 总中断允许 */ /* 等待中断 */ 1.1.2 程序清单 下面给出一个调试通过的例程, 可作为读者的参考 调试该程序把模板 J7 上的短路跳 针拔下, 以免产生冲突 #include <pic1687x.h> volatile unsigned char data /*spi 初始化子程序 */ void SPIINIT() PIR1=0 SSPCON=0x30 /* SSPEN=1CKP=0, FOSC/4 */ SSPSTAT=0xC0 TRISC=0x10 /*SDI 引脚为输入,SCK 引脚为输出 */ /* 系统各输入输出口初始化子程序 */ void initial() TRISA=0x00 TRISD=0x00 /*D 口为输出方式 */ INTCON=0x00 /* 关闭所有中断 */ 95
/*SPI 接收子程序 */ int SPIIN() RA4=0 /*74HC165 并行置数使能, 将 8 位开关量置入器件 */ /* (LOAD 为低电平时 8 位并行数据置入 74HC165)*/ RA4=1 /*74HC165 移位置数使能 (LOAD 为高电平时芯 */ /* 片才能串行工作 )*/ SSPBUF=0 /* 启动 SPI, 此操作只用于清除 SSPSTAT 的 *BF 位, 因此 W 中的实际数据无关紧要 */ do while(sspif==0) /* 查询数据接收完毕否?*/ SSPIF=0 data=sspbuf return(data) /* 返回接收到的数据 */ /* 把 SPI 接收的数据通过 D 口显示在 8 个发光二极管上的子程序 */ void SPIOUT(int data) PORTD=~data /* 主程序 */ main( ) initial() /* 系统初始化 */ SPIINIT() /*SPI 初始化 */ SPIIN() /*SPI 接收外部数据 */ SPIOUT(data) /* 送出数据显示 */ 1.6.1 CCP 模块的 PWM 工作方式简介下面给出一个 CCP 模块设置为 PWM 操作时的初始化程序例 1.4 CCP 模块设置为 PWM 方式时的初始化程序 /*CCP1 模块的 PWM 工作方式初始化子程序 */ void CCP1INIT() 96
CCPR1L=0X7F CCP1CON=0X3C /* 设置 CCP1 模块为 PWM 工作方式, 且其工作循环 * 的低 2 位为 11, 高 8 位为 01111111=7F*/ INTCON=0X00 /* 禁止总中断和外围中断 */ PR2=0XFF /* 设置 PWM 的工作周期 */ TRISC=0XFB /* 设置 CCP1 引脚为输出方式 */ 该初始化子程序设置 CCP1 模块输出分辨率为 10 位的 PWM 波形, 且占空比为 50% 1.6.3 程序清单下面给出一个调试通过的例程, 可作为读者编制程序的参考 #include <pic.h> /* 该程序用于使 CCP1 模块产生分辨率为 10 位的 PWM 波形, 占空比为 50%*/ /*CCP1 模块的 PWM 工作方式初始化子程序 */ void CCP1INIT() CCPR1L=0X7F CCP1CON=0X3C /* 设置 CCP1 模块为 PWM 工作方式, 且其工作 * 循环的低 2 位为 11, 高 8 位为 01111111=7F*/ INTCON=0X00 /* 禁止总中断和外围中断 */ PR2=0XFF /* 设置 PWM 的工作周期 */ TRISC=0XFB /* 设置 CCP1 引脚为输出方式 */ /* 主程序 */ main() CCP1INIT() /*CCP1 模块的 PWM 工作方式初始化 */ T2CON=0X04 /* 打开 TMR2, 且使其前分频为 0, * 同时开始输出 PWM 波形 */ do /* 系统开始输出 PWM 波形 如果系统是 * 多任务的, 则可以在此执行其它任务, 而 * 不会影响 PWM 波形的产生 */ 97
1.7.3 应用程序 2. 程序清单 #include <pic.h> /* 此程序实现 " 看门狗 "WDT 的功能 */ unsigned long I /* 系统初始化子程序 */ void initial() OPTION = 0X0F /* 把前分频器分配给 WDT, 且分频倍率为 1:128*/ TRISD = 0X00 /*D 口设为输出 */ /* 延时子程序 */ void DELAY() for (i=19999--i) continue /* 主程序 */ main () initial() /* 初始化, 设定看门狗的相关寄存器 */ PORTD = 0X00 /*D 口送 00H, 发光二极管亮 */ DELAY() /* 给予一定时间的延时 */ PORTD = 0XFF /*D 口送 FFH, 发光二极管灭 */ /* 死循环, 等待看门狗溢出复位 */ 1.8.3 程序清单 该例在 PIC16F877 休眠前使 8 个发光二极管的高 4 个发光, 然后进入休眠工作方式 若按键引起的中断将其激活, 则低 4 个发光 用 C 语言编写程序时, 语句 SLEEP() 相当 于汇编语言中的语句 sleep, 使单片机进入休眠状态 #include <pic.h> /* 该程序实现 PIC16F877 的休眠工作方式, 并由实验板上的按键产生 " 电平变化中断 " 将其 * 98
从休眠状态中激活 休眠与激活的状态由与 D 口相连的 8 个 LED 显示 休眠时高 4 个 *LED 发光, 低 4 个 LED 熄灭 激活以后高 4 个 LED 熄灭, 低 4 个 LED 发光 */ unsigned long i /* 系统初始化子程序 */ void initial() di() /* 全局中断禁止," 电平变化中断 " 只执行唤醒功能 */ RBIE=1 /*PORTB 口电平变化中断允许 */ RBIF=0 /* 清除 B 口电平变化中断标志 */ TRISB4=1 TRISB5=1 TRISB2=0 TRISB1=0 /* 设置与键盘有关的各 I/O 口的输入输出方式 */ TRISD=0X00 /*D 口为输出 */ PORTB=0X00 /* 键盘的行线送低电平, 为 电平变化中断 作准备 */ PORTB=PORTB /* 读 PORTB 的值, 锁存旧值, 也为 电平变化 * 中断 作准备 */ /* 主程序 */ main () initial() /* 初始化 */ PORTD=0X0F /* 高 4 个 LED 灯亮 */ SLEEP() /* 单片机开始进入休眠状态 */ PORTD=0XF0 /* 激活后, 低 4 个 LED 灯亮 */ 99