资源描述
PIC单片机应用开发典型模块第四章程序
27
2020年4月19日
资料内容仅供参考,如有不当或者侵权,请联系本人改正或者删除。
( 1) 多功能波形信号发生模块
程序应用
#INCLUDE<P16F877.INC>
RTCC EQU 01H
PC EQU 02H
PCLATH EQU 0AH
TEMP0TIME EQU 20H ;方波次数
TEMP0CONST EQU 21H ;方波常数
TEMO0DOT EQU 22H ;方波点数
TEMP0OUT EQU 23H ;方波输出值
TEMP1TIME 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暂存
RCC EQU 0FAH ;最大频率常数
;------------------------------------------------------------------------------------------------------
ORG 0
SYS_RESET
GOTO START
;------------------------------------------------------------------------------------------------------
ORG 4 ;中断程序
MOVWF WBU
SWAPF 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 ;延时
NOP
NOP
GOTO LOOP
;------------------------------------------------------------------------------------------------------
INT
CLRF TEMP0DOT
CLRF TEMP1DOT
CLRF TEMP2DOT
CLRF TEMP0OUT
CLRF TEMP1OUT
CLRF TEMP2OUT
MOVLW 03H
MOVWF TEMP0TIME
MOVWF TEMP0CONST
MOVWF TEMP2TIME
MOVWF TEMP2CONST
MOVLW 06H
MOVWF TEMP1TIME
MOVWF TEMP1CONST
RETURN
;------------------------------------------------------------------------------------------------------
INTPORT ;端口初始化
BCF STATUS, 0
BCF STATUS, 1
BCF STATUS, 5
CLRF PORTC
CLRF PORTD
BSF PORTE,1
BSF STATUS, 5
MOVLW 00H
MOVWF TRISD
MOVLW OF4H
MOVWF TRISC
;定义RE1为输出, 其它为输入
MOVWF TRISE
BCF STATUS, 5
RETURN
;------------------------------------------------------------------------------------------------------
INTTM0 ;定时器初始化
BSF STATUS, 5
MOVLW 80H
MOVWF OPTION_ R
BCF STATUS, 5
MOVLW 0A0H
;开放定时器中断
MOVLW RCC ;RCC=最大频率延时常数
MOVWF RTCC
RETURN;
;------------------------------------------------------------------------------------------------------
SERY_ INT ;中断服务程序
BTFSC INTCON, 2
GOTO SERVRTCC
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 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
BCF PORTC, 1 ;选择输出通道, 0通道为方波
INCF TEMP0DOT, 1
MOVF TEMP0CONST, 0
MOVWF TEMP0TIME
BTFSC TEMP0DOT, 7
GOTO GAO
GOTO DI
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
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 ;选择输出通道, 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 TEMP2OUT
RETURN
END
( 2) PWM信号发生模块
程序应用
#include <pic.h>
/*CCP1模块的PWM工作方式初始化子程序*/
void INIT_CCP1()
{
PR2=0XFF; /*设置PWM周期*/
CCPR1L=0X7F; /*设置工作循环的高8bit为01111111*/
CCP1CON=0X3C; /*设置CCP1为PWM工作方式, 且工作循环的低bit2为11*/
INTCON=0X00; /*禁止总中断和外围中断*/
TRISC=0XFB; /*设置RC2/CCP1为输出*/
}
/*主程序*/
main()
{
INIT_CCP1(); /*CCP1模块的PWM工作方式初始化*/
T2CON=0X03; /*前分频比为16, 打开TMR2, 同时输出PWM信号*/
}
( 3) 正弦信号发生模块
程序应用
#include <pic.h>
//本程序将经过PIC16F877的I2C方式驱动D/A 转换器MAX518, 使其D/A0 通道输出
//一个连续的正弦波形
const char table[ ] = {0X80, 0X86, 0X8D, 0X93, 0X99, 0X9F, 0XA5, 0XAB,
0XB1, 0XB7, 0XBC, 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, 0X96, 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, 0X1E, 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 寄存器
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; //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); //等待地址发送完毕
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); //等待停止条件产生
SSPIF=0; //SSPIF 标志清0
}
//主程序
main ()
{
i2cint(); //I2C 初始化
while(1){
for(i=0x00; i<=127; ++i)
{
j=table[i]; //从数组中得到需要传输的数据量
i2cout(); //利用I2C 总线方式送出数据
}
}
( 4) 简易频率计模块程序
#include <pic.h>
#include <stdio.h>
#include <math.h>
bank3 int cp1z[11]; //定义一个数组, 用于存放各次的捕捉值
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;
double puad5;
//CCP 模块工作于捕捉方式初始化子程序
void ccpint( )
{
CCP1CON=0X05; //首先设置CCP1 捕捉每个脉冲的上升沿
T1CON=0X00; //关闭TMR1 震荡器
PEIE=1; //外围中断允许(此时总中断关闭)
CCP1IE=1; //允许CCP1 中断
TRISC2=1; //设置RC2 为输入
}
//系统其它部分初始化子程序
void initial( )
{
COUNT=0X0B; //为保证测试精度, 测试5 个脉冲的参数后
//求平均值, 每个脉冲都要捕捉其上升、 下降沿, 故需要有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[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=cp1z[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(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;
}
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;
} //等待中断次数结束
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中
OCCUPATIONAL(); //进行占空比处理
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 <pic.h>
#include <math.h>
#include <stdio.h>
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 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; //禁止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;
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; //定时器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=(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中
}
展开阅读全文