1、 174 第 10 章 利用 CCP 模块设计频率计 10.5 程序设计 10.5.4 程序清单 #include #include #include /本程序利用 CCP1 模块实现一个“简易数字频率计” 的 功能 const char table11=0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0XD8, 0x80, 0x90,0xFF; /不带小数点的 显示段码表 const char table011=0X40, 0X79, 0X24, 0X30, 0X19, 0X12, 0X02, 0X78, 0X00,0X10, 0xFF; /带小数点的 显
2、示段码表 bank3 int cp1z11; /定义一个数组 , 用于存放各次的捕捉值 union cp1 int y1; unsigned charcp1e2; cp1u; /定义一个共用体 unsigned char COUNTW, COUNT; /测量脉冲个数寄存器 unsigned char COUNTER, data, k; unsigned char FLAG 0XEF; #define FLAGIT(adr, bit) (unsigned)(&adr)*8+(bit) /绝对寻址位操作指令 staticbit FLAG1 FLAGIT(FLAG, 0); staticbit FL
3、AG2 FLAGIT(FLAG, 1); staticbit FLAG3 FLAGIT(FLAG, 2); unsigned char s4; /定义一个显示缓冲数组 int T5 , uo; double RE5; double puad5; /spi方式显示初始化子程序 void SPIINIT() PIR1=0; SSPCON=0x30; SSPSTAT=0xC0; 175 /设置 SPI 的控制方式,允许 SSP方式,并且时钟下降沿发送,与 “74HC595, 当其 /SCLk 从低到高跳变时,串行输入寄存器 “的特点相对应 TRISC=0xD7; /SDO 引脚为输 出 , SCK引
4、脚为输出 TRISA5=0; /RA5 引脚设置为输出 , 以输出显示锁存信号 FLAG1=0; FLAG2=0; FLAG3=0; COUNTER=0X01; /CCP 模块工作于捕捉方式初始化子程序 void ccpint( ) CCP1CON=0X05; /首先设置 CCP1 捕捉每个脉冲的上升沿 T1CON=0X00; /关闭 TMR1 震荡器 PEIE=1; /外围中断允许 (此时总中断关闭 ) CCP1IE=1; /允许 CCP1 中断 TRISC2=1; /设置 RC2 为输入 /系统其它部分初始化子程序 void initial( ) COUNT=0X0B; /为 保证测试精度
5、 , 测试 5 个脉冲的参数后 /求平均值 , 每个脉冲都要捕捉其上升 、 下降沿 , /故需要有 11 次中断 TRISB1=0; TRISB2=0; TRISB4=1; TRISB5=1; /设置与键盘有关的各口的输入 、 输出方式 RB1=0; RB2=0; /建立键盘扫描的初 始条件 /SPI 传送数据子程序 void SPILED(data) SSPBUF=data; /启动发送 do ; while(SSPIF=0); 176 SSPIF=0; /显示子程序 , 显示 4 位数 void display( ) RA5=0; /准备锁存 for(COUNTW=0; COUNTW4)
6、COUNTER=0x01; /若 COUNTER 超过 4, 则又从 1 计起 177 if(data=0x02) COUNTER=COUNTER-1; /若按下 S11 键 , 则 COUNTER 减 1 if(COUNTER1) COUNTER=0x04; /若 COUNTER 小于 1, 则又循环从 4 计起 if(data=0x03) FLAG2=1; /若按下 S10 键 , 则建立标志 FLAG2 if(data=0x04) FLAG2=0; /若按下 S12 键 , 则清除标志 FLAG2 /中断服务程序 void interrupt cp1int(void) CCP1IF=0;
7、 /清除中断标志 cp1u.cp1e0=CCPR1L; cp1u.cp1e1=CCPR1H; cp1zdata=cp1u.y1; /存储 1 次捕捉值 CCP1CON=CCP1CON0X01; /把 CCP1 模块改变成捕捉相反的脉冲沿 data+; COUNT-; /周期处理子程序 void PERIOD( ) T5=cp1z10-cp1z0; /求得 5 个周期的值 RE5=(double)T5; /强制转换成双精度数 RE5=RE5/5; /求得平均周期 , 单位为 s /频率处理子程序 void FREQUENCY( ) PERIOD( ); /先求周期 RE5=1000000/RE5
8、; /周期值求倒数 , 再乘以 1 000 000, 得频率 , /单位为 HZ /脉宽处理子程序 void PULSE( ) int pu; for(data=0, puad5=0; data=9; data+) 178 pu=cp1zdata+1-cp1zdata; puad5=(double)pu+puad5; data=data+2; /求得 5 个脉宽的和值 RE5=puad5/5; /求得平均脉宽 /占空比处理子程序 void OCCUPATIONAL( ) PULSE( ); /先求脉宽 puad5=RE5; /暂存脉宽值 PERIOD(); /再求周期 RE5=puad5/RE
9、5; /求 得 占空比 /主程序 main( ) SPIINIT( ); /SPI 方式显示初始化 while(1) ccpint(); /CCP 模块工作于捕捉方式初始化 initial(); /系统其它部分初始化 if(FLAG2=0) s0=COUNTER; /第一个存储 COUNTER 的值 s1=0X0A; s2=0X0A; s3=0X0A; /后面的 LED 将显示 “DARK“ display( ); /调用显示子程序 keyscan(); /键盘扫描 data=0x00; /存储数组指针赋初值 TMR1H=0; TMR1L=0; /定时器 1 清 0 CCP1IF=0; /清除
10、 CCP1 的中断标志,以免中断一打开就进入 /中断 ei( ); /中断允许 TMR1ON=1; /定时器 1 开 while(1) if(COUNT=0)break; 179 /等待中断次数结束 di(); /禁止中断 TMR1ON=0; /关闭定时器 keyscan(); /键盘扫描 if(FLAG1=1) keyserve(); /若确实有键按下 , 则调用键服务程序 if(FLAG2=0) continue; /如果没有按下确定键 , 则终止此次循环 , /继续进行测量 /如果按下了确定键 , 则进行下面的数值转换和显示工作 if(COUNTER=0x01) FREQUENCY();
11、 /COUNTER=1, 则需要进行频率处理 if(COUNTER=0x02) PERIOD(); /COUNTER=2, 则需要进行周期处理 if(COUNTER=0x03) OCCUPATIONAL(); /COUNTER=3, 则需要进行占空比处理 if(COUNTER=0x04) PULSE(); /COUNTER=4, 则需要进行脉宽处理 k=5; if(RE51) RE5=RE5*1000; /若 RE51, 则乘以 1 000, 保证小数点的精度 k=0x00; else if(RE510) RE5=RE5*1000; /若 RE510, 则乘以 1 000, 保证小数点的精度 k=0x00; else if(RE5100) RE5=RE5*100; /若 RE5100, 则乘以 100, 保证小数点的精度 k=0x01; else if(RE51000) RE5=RE5*10; /若 RE51000, 则乘以 10, 保证小数点的精度 k=0x02; else RE5=RE5; uo=(int)RE5; sprintf(s, “%4d“, uo); /把需要显示的数据转换成 4 位 ASII码 , 且放入数 /组 S 中 display();