1、
PIC单片机应用开发典型模块第四章程序
27
2020年4月19日
资料内容仅供参考,如有不当或者侵权,请联系本人改正或者删除。
( 1) 多功能波形信号发生模块
程序应用
#INCLUDE
2、P1TIME EQU 30H ;锯齿波次数 TEMP1CONST EQU 31H ;锯齿波常数 TEMP1 DOT EQU 32H ;锯齿波点数 TEMP1OUT EQU 33H ;锯齿波输出值 TEMP2TIME EQU 40H ;三角波次数 TEMP2CONST EQU 41H ;三角波常数 TEMP2 DOT EQU 42H ;三角波点数 TEMP2OUT EQU 43H ;三角波输出值 OPTION_R EQU 81H WBU EQU 2FH ;W暂存 SBU EQU 2EH ;STATUS暂存 R
3、CC EQU 0FAH ;最大频率常数 ;------------------------------------------------------------------------------------------------------ ORG 0 SYS_RESET GOTO START ;------------------------------------------------------------------------------------------------------ ORG 4 ;中断程序 MOVWF WBU SW
4、APF WB0, 0 SWAPF STATUS, 0 MOVWF SBU CALL SERV_INT SWAPF SBU, 0 MOVWF STATUS SWAPF WBU, 0 RETFIE ;------------------------------------------------------------------------------------------------------ ORG 40H NOP START CALL INT CALL INTPORT CALL INTTMO LOOP ;延时
5、 NOP NOP GOTO LOOP ;------------------------------------------------------------------------------------------------------ INT CLRF TEMP0DOT CLRF TEMP1DOT CLRF TEMP2DOT CLRF TEMP0OUT CLRF TEMP1OUT CLRF TEMP2OUT MOVLW 03H MOVWF TEMP0TIME MOVWF TEMP0CONST MOVWF TEMP2T
6、IME MOVWF TEMP2CONST MOVLW 06H MOVWF TEMP1TIME MOVWF TEMP1CONST RETURN ;------------------------------------------------------------------------------------------------------ INTPORT ;端口初始化 BCF STATUS, 0 BCF STATUS, 1 BCF STATUS, 5 CLRF PORTC CLRF PORTD BSF P
7、ORTE,1 BSF STATUS, 5 MOVLW 00H MOVWF TRISD MOVLW OF4H MOVWF TRISC ;定义RE1为输出, 其它为输入 MOVWF TRISE BCF STATUS, 5 RETURN ;------------------------------------------------------------------------------------------------------ INTTM0 ;定时器初始化 BSF STATU
8、S, 5 MOVLW 80H MOVWF OPTION_ R BCF STATUS, 5 MOVLW 0A0H ;开放定时器中断 MOVLW RCC ;RCC=最大频率延时常数 MOVWF RTCC RETURN; ;------------------------------------------------------------------------------------------------------ SERY_ INT ;中断服务程序 BTFSC INTCON, 2 GOTO SERVRT
9、CC CLRF INTCON BSF INTCON, 5 RETURN SERVRTCC MOVLW RCC MOVWF RTCC BCF INTCON, 2 CALL OUTPUT RETFIE ;------------------------------------------------------------------------------------------------------ OUTPUT ;输出波形 BCF STATUS,5 FBO DECFSZ TEMP0TIME, 1 ;方波 GOTO
10、J BO CALL FANGBO JBO DECFSZ TEMP1TIME, 1 ;锯齿波 GOTO SJ BO CALL JUCBO SJBO DECFSZ TEMP2 TIME,1 ;三角波 RETURN CALL SJIAOBO RETURN ;------------------------------------------------------------------------------------------------------ FANGBO BCF PORTS, 1 ;为低 BCF PORTC, 0
11、 BCF PORTC, 1 ;选择输出通道, 0通道为方波 INCF TEMP0DOT, 1 MOVF TEMP0CONST, 0 MOVWF TEMP0TIME BTFSC TEMP0DOT, 7 GOTO GAO GOTO DI
12、 GAO MOVLW OFFH MOVWF PORTD BSF PORTE, 1 RETURN DI M0VLW 00H MOVWF PORTD BSF PORTE, 1 RETURN ;------------------------------------------------------------------------------------------------------ JUCBO BCF PORTE, 1 BCF PORTC, 0 ;选择输出通道, 1通道为锯齿波 MOVF TEMP1CONST, 0
13、MOVWF TEMP1TIME INCFSZ TEMP1DOT, 1 GOTO JUBO1 MOVF TEMP1OUT, 0 MOVWF PORTD INCF TEMP1OUT, 1 RETURN JUBO1 MOVLW 00H MOVWF TEMPlOUT RETURN ;------------------------------------------------------------------------------------------------------ SJIAOBO BCF PORTE, 1 BCF PORTC, 1
14、 ;选择输出通道, 2通道为三角波 MOVF TEMP2CONST, 0 MOVWF TEMP2TIME INCFSZ TEMP2DOT, 1 GOTO SJBO2 CALL SJBO0 SJB02 MOVF TEMP2OUT, 0 MOVWF PORTD BSF PORTE, 1 MOVLW 02H BTFSS TEMP2DOT, 7 GOTO SJBO1 ADDWF TEMP2OUT, 1 RETURN SJBO1 SUBWF TEMP2OUT, 1 RETURN SJBO0 CLRF TEMP2
15、OUT
RETURN
END
( 2) PWM信号发生模块
程序应用
#include
16、主程序*/
main()
{
INIT_CCP1(); /*CCP1模块的PWM工作方式初始化*/
T2CON=0X03; /*前分频比为16, 打开TMR2, 同时输出PWM信号*/
}
( 3) 正弦信号发生模块
程序应用
#include
17、BC, 0XC2, 0XC7, 0XCC, 0XD1, 0XD6, 0XDA, 0XDF, 0XE3, 0XE7, 0XEA, 0XEE, 0XF1, 0XF4, 0XF6, 0XF8, 0XFA, 0XFC, 0XFD, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFE, 0XFD, 0XFB, 0XF9, 0XF7, 0XF5, 0XF2, 0XEF, 0XEC, 0XE9, 0XE5, 0XE1, 0XDD, 0XD8, 0XD4, 0XCF, 0XCA, 0XC5, 0XBF, 0XBA, 0XB4, 0XAE, 0XA8, 0XA2, 0X9C, 0X
18、96, 0X90, 0X89, 0X83, 0X80, 0X79, 0X72, 0X6C, 0X66, 0X60, 0X5A, 0X55, 0X4E, 0X48, 0X43, 0X3D, 0X38, 0X33, 0X2E, 0X29, 0X25, 0X20, 0X1C, 0X18, 0X15, 0X11, 0X0E, 0X0B, 0X09, 0X07, 0X05, 0X03, 0X02, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X02, 0X04, 0X06, 0X08, 0X0A, 0X0D, 0X10, 0X13, 0X16, 0X1A, 0X
19、1E, 0X22, 0X27, 0X2B, 0X30, 0X35, 0X3A, 0X40, 0X45, 0X4C, 0X51, 0X57, 0X5D, 0X63, 0X69, 0X6F, 0X76, 0X7C}; //以上的数组用于存放正弦表, 在定义数组时, 前面应该加上 const, //使数组存放于ROM 中, 而不至于占用太多的RAM unsigned char i; unsigned char j; unsigned char n; //I2C 初始化子程序 void i2cint() { SSPCON = 0X08; //初始化SSPCON 寄存器
20、 TRISC3 =1; //设置SCL 为输入口 TRISC4 =1; //设置SDA 为输入口 TRISA4 = 0; SSPSTAT=0X80; //初始化SSPSTAT 寄存器 SSPADD=0X02; //设定I2C 时钟频率 SSPCON2=0X00; //初始化SSPCON2 寄存器 di(); //关闭总中断 SSPIF=0; //清SSP 中断标志 RA4=0; //关掉74HC165 的移位时钟使能, 以免74HC165 //移位数据输出与I2C 总线的数据线发生冲突 SSPEN=1;
21、 //SSP模块使能 } //I2C 总线输出数据子程序 void i2cout() { SEN=1; //产生I2C 启动信号 for(n=0x02; --n; ) continue; //给予一定的延时, 保证启动 do { RSEN=1; //产生I2C 启动信号 }while(SSPIF==0); //如果没能启动, 则重复启动, 直到启动为止 SSPIF=0; //SSPIF 标志清0 SSPBUF=0X58; //I2C 总线发送地址字节 do { ; }while(SSPIF==0); //
22、等待地址发送完毕 SSPIF=0; //SSPIF 标志清0 SSPBUF=0X01; //I2C 总线发送命令字节 do { ; }while(SSPIF==0); //等待命令发送完毕 SSPIF=0; //SSPIF 标志清0 SSPBUF=j; //I2C 总线发送数据字节 do { ; }while(SSPIF==0); //等待数据发送完毕 SSPIF=0; //SSPIF 标志清0 PEN=1; //产生停止条件 do { ; }while(SSPIF==0); //等待
23、停止条件产生
SSPIF=0; //SSPIF 标志清0
}
//主程序
main ()
{
i2cint(); //I2C 初始化
while(1){
for(i=0x00; i<=127; ++i)
{
j=table[i]; //从数组中得到需要传输的数据量
i2cout(); //利用I2C 总线方式送出数据
}
}
( 4) 简易频率计模块程序
#include
24、一个数组, 用于存放各次的捕捉值 union cp1 { int y1; unsigned char cp1e[2]; }cp1u; //定义一个共用体 unsigned char COUNTW, COUNT; //测量脉冲个数寄存器 unsigned char COUNTER, data, k; unsigned char s1[4]; //定义4个显示缓冲数组 unsigned char s2[4]; unsigned char s3[4]; unsigned char s4[4]; int T5, uo; double RE5;
25、double puad5; //CCP 模块工作于捕捉方式初始化子程序 void ccpint( ) { CCP1CON=0X05; //首先设置CCP1 捕捉每个脉冲的上升沿 T1CON=0X00; //关闭TMR1 震荡器 PEIE=1; //外围中断允许(此时总中断关闭) CCP1IE=1; //允许CCP1 中断 TRISC2=1; //设置RC2 为输入 } //系统其它部分初始化子程序 void initial( ) { COUNT=0X0B; //为保证测试精度, 测试5 个脉冲的参数后
26、 //求平均值, 每个脉冲都要捕捉其上升、 下降沿, 故需要有11 次中断 } //中断服务程序 void interrupt cp1int(void) { CCP1IF=0; //清除中断标志 cp1u.cp1e[0]=CCPR1L; cp1u.cp1e[1]=CCPR1H; cp1z[data]=cp1u.y1; //存储1 次捕捉值 CCP1CON=CCP1CON^0X01; //把CCP1 模块改变成捕捉相反的脉冲沿 data++; COUNT--; } //周期处理子程序 void PERIOD( ) { T5=cp1z[
27、10]-cp1z[0]; //求得5 个周期的值 RE5=(double)T5; //强制转换成双精度数 RE5=RE5/5; //求得平均周期, 单位为μs } //频率处理子程序 void FREQUENCY( ) { PERIOD( ); //先求周期 RE5=1000000/RE5; //周期值求倒数, 再乘以1000000, 得频率, 单位为Hz } //脉宽处理子程序 void PULSE( ) { int pu; for(data=0, puad5=0; data<=9; data++) { pu=cp1
28、z[data+1]-cp1z[data]; puad5=(double)pu+puad5; data=data+2; } //求得5 个脉宽的和值 RE5=puad5/5; //求得平均脉宽 } //占空比处理子程序 void OCCUPATIONAL( ) { PULSE( ); //先求脉宽 puad5=RE5; //暂存脉宽值 PERIOD(); //再求周期 RE5=puad5/RE5; //求得占空比 } precision(RE5) //为了保证小数点的精度, 对RE5进行处理 { k=5; if
29、RE5<1) { RE5=RE5*1000; //若RE5<1, 则乘以1 000, 保证小数点的精度 k=0x00; } else if(RE5<10) { RE5=RE5*1000; //若RE5<10, 则乘以1 000, 保证小数点的精度 k=0x00; } else if(RE5<100) { RE5=RE5*100; //若RE5<100, 则乘以100, 保证小数点的精度 k=0x01; } else if(RE5<1000) { RE5=RE5*10; //若RE5<1000, 则乘以10, 保证小数点的精度 k=0x02;
30、 } else RE5=RE5 ; } //主程序 main( ) { ccpint(); //CCP模块工作于捕捉方式初始化 initial(); //系统其它部分初始化 data=0x00; //存储数组指针赋初值 TMR1H=0; TMR1L=0; //定时器1 清0 CCP1IF=0; //清除CCP1 的中断标志, 以免中断一打开就进入 //中断 ei( ); //中断允许 TMR1ON=1; //定时器1 开 while(1) { if(COUNT==0)break; } //等待中断次数结束
31、di(); //禁止中断 TMR1ON=0; //关闭定时器 //进行下面的数值转换和显示工作 PERIOD(); //进行周期处理 precision(RE5); uo=(int)RE5; sprintf(s1, "%4d", uo); //把周期数据转换成4 位ASII 码, 且放入数组S1中 FREQUENCY(); //进行频率处理 precision(RE5); uo=(int)RE5; sprintf(s2, "%4d", uo); //把频率数据转换成4 位ASII 码, 且放入数组S2中 OCCUPATIONA
32、L(); //进行占空比处理
precision(RE5);
uo=(int)RE5;
sprintf(s2, "%4d", uo); //把占空比数据转换成4 位ASII 码, 且放入数组S3中
PULSE(); //进行脉宽处理
precision(RE5);
uo=(int)RE5;
sprintf(s2, "%4d", uo); //把脉宽数据转换成4 位ASII 码, 且放入数组S4中
}
( 5) 交流电压测量模块程序
#include
33、union adres { inty1; unsigned charadre[2]; }adresult; //定义一个共用体 bank3 int re[40]; //定义存放A/D转换结果的数组, 在bank3中 unsigned char k, data; //定义几个通用寄存器 double squ,squad; //平方寄存器和平方和寄存器, squ又通用为存储其它数值 int uo; bank1 unsigned char s[4]; //此数组用于存储需要显示的字符的ASII码 //A/D转换初始化子程序 void
34、adinitial() { ADCON0=0x41; //选择A/D通道为RA0, 且打开A/D转换器 //在工作状态, 使A/D转换时钟为8Tosc ADCON1=0X8E; //转换结果右移, 及ADRESH寄存器的高6位为"0" //把RA0口设置为模拟量输入方式 ADIE=1; //A/D转换中断允许 PEIE=1; //外围中断允许 TRISA0=1; //设置RA0为输入方式 } //系统其它初始化子程序 voidinitial() { CCP2IE=0;
35、 //禁止CCP中断 SSPIE=0; //禁止SSP中断 CCP2CON=0X0B; //初始化CCP2CON, CCP2为特别事件触发方式 CCPR2H=0X01; CCPR2L=0XF4; //初始化CCPR2寄存器, 设置采样间隔500 μs, //一个周期内电压采40个点 } //中断服务程序 void interruptadint(void) { CCP2IF=0; ADIF=0; //清除中断标志 adresult.adre[0]=ADRESL;
36、 adresult.adre[1]=ADRESH; //读取并存储A/D转换结果, A/D转换的结果 //经过共用体的形式放入了变量y1中 re[k]=adresult.y1; //1次A/D转换的结果存入数组 k++; //数组访问指针加1 } //主程序 main( ) { adinitial(); //A/D转换初始化 initial(); //系统其它初始化 k=0; //数组访问指针赋初值 TMR1H=0X00; TMR1L=0X00; //定时
37、器1清0 ei(); //中断允许 T1CON=0X01; //打开定时器1 while(1) { if(k==40)break; //A/D转换次数达到40, 则终止 } di(); //禁止中断 for(k=0; k<40; k++)re[k]=re[k]-0X199; //假设提升电压为2 V, 对应十六进制数199H, 则 //需在采样值的基础上减去该值 for(k=0, squad=0; k<40; k++){ uo=re[k]; squ=(
38、double)uo; //强制把采得的数据量转换成双精度数, 以便运算 squ=squ*5/1023; //把每点的数据转换成实际数据 squ=squ*squ; //求一点电压的平方 squad=squad+squ; }//以上求得40点电压的平方和, 存于寄存器 squad中 squ=squad/40; //求得平均值 squ=sqrt(squ); //开平方, 求得最后的电压值 squ=squ*154.054; //经过变压器的变比和分压电阻分配确定该系数 //以上得到了实际电网的电压值 squ=squ*10; //为了保证显示的小数点的精度, 先对电压值乘以10 uo=(int)squ; //强制把U转换成有符号整型量 sprintf(s, "%4d", uo); //经过sprintf函数把需要显示的电压数据转换成 ASII码, 并存于数 //组S中 }






