5.1.2 简单应用实例
该例用于令与PORTD口相连的8个发光二极管前4个点亮,后4个熄灭。在调试程序前,应使与PORTD口相连的8位拔码开关拔向相应的位置。
例5.1 PORTD输出
#include <pic.h>
main()
{
TRISD=0X00; /*TRISD寄存器被赋值,PORTD每一位都为输出*/
while(1); /*循环执行点亮发光二极管的语句*/
{
PORTD=0XF0; /*向PORTD送数据,点亮LED(由实验模板*/
/*的设计决定相应位置低时LED点亮)。*/
}
}
5.2.1 MSSP模块SPI方式功能简介
下面是一段简单的SPI初始化例程,用于利用SPI工作方式输出数据的场合。
例5.2 SPI初始化程序
/*spi初始化子程序*/
void SPIINIT()
{
PIR1=0; /*清除SPI中断标志*/
SSPCON=0x30; /* SSPEN=1;CKP=0 , FOSC/4 */
SSPSTAT=0xC0;
TRISC=0x00; /*SDO引脚为输出,SCK引脚为输出*/
}
5.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)) /*绝对寻址位操作指令*/
static bit PORTA_5 @ PORTAIT(PORTA,5);
/*spi初始化子程序*/
void SPIINIT()
{
PIR1=0;
SSPCON=0x30; /* SSPEN=1;CKP=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=8;i>0;i--) /*连续发送8个数据*/
{
data=table[i]; /*通过数组的转换获得待显示的段码*/
SPILED(data); /*发送显示段码显示*/
}
PORTA_5=1; /*最后给锁存信号,代表显示任务完成*/
}
5.3.3 程序清单
下面给出已经在实验板上调试通过的程序,可作为用户编制其它程序的参考。有关显示部分的SPI初始化,请读者参考5.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()
{
for(i = 6553; --i ;)
continue;
}
/*键扫描子程序*/
int KEYSCAN()
{
while(1)
{
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的值*/
while(1)
{
if((PORTB_5==1)&&(PORTB_4==1)) break;/*等待键盘松开*/
}
return(j);
}
/*SPI发送子程序*/
void SPILED(int data)
{
SSPBUF=data; /*启动发送*/
do
{
;
}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初始化*/
while(1)
{
KEYSCAN();
if(j!=0) /*如果j=0,证明先前的按键为干扰,则不予显示*/
{
data=table[j];
PORTA_5=0; /*LACK信号清0,为锁存做准备*/
SPILED(data);
PORTA_5=1; /*最后给锁存信号,代表显示任务完成*/
}
}
}
5.4.1 PORTB端口“电平变化中断”简介
例5.3 PORTB口“电平变化中断”初始化子程序
/*B口“电平变化中断”初始化子程序*/
void PORTBINT( )
{
TRISB=0XF0; /*设置相应口的输入输出方式*/
OPTION=0x7F; /*B口弱上拉有效*/
PORTB=0X00; /*RB1,RB2 先送低电平*/
RBIE=1; /*B口变位中断允许 */
PORTB=PORTB; /*读B口的值,以锁存旧值,为变位中断创造条件*/
}
5.4.3 程序清单
下面给出一个调试通过的例程,以供读者参考。有关显示的部分请读者参考前面章节。该程序中寄存器的位都用头文件中定义的位,如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 SPIINIT()
{
PIR1=0;
SSPCON=0x30;
SSPSTAT=0xC0;
TRISC=0xD7; /*SDO引脚为输出,SCK引脚为输出*/
}
/*系统各输入输出口初始化子程序*/
void initial()
{
TRISA=0xDF;
INTCON=0x00; /*关闭所有中断*/
data=0X00; /*待显示的寄存器赋初值*/
}
/*键服务子程序*/
void KEYSERVE()
{
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)) /*该语句除了能够确认按键是否为干扰外,*/
/*还可以屏蔽一次键松开时引起的中断*/
IDEDIS(); /*键识别和显示模块*/
PORTB=PORTB; /*读B口的值,改变中断发生的条件,避免键*/
/*一直按下时,连续进行键识别*/
RBIF=0; /*键扫描时可能会产生"电平变化"而使RBIF*/
/*置1,再清除一次RBIF以避免额外中断*/
}
main()
{
initial(); /*系统初始化*/
PORTBINT(); /*B口变位中断初始化*/
SPIINIT() ; /*利用SPI显示初始化*/
ei(); /*总中断允许*/
while(1)
{
;
} /*等待中断*/
}
5.5.2 程序清单
下面给出一个调试通过的例程,可作为读者的参考。调试该程序把模板J7上的短路跳针拔下,以免产生冲突。
#include <pic1687x.h>
volatile unsigned char data;
/*spi初始化子程序*/
void SPIINIT()
{
PIR1=0;
SSPCON=0x30; /* SSPEN=1;CKP=0 , FOSC/4 */
SSPSTAT=0xC0;
TRISC=0x10; /*SDI引脚为输入,SCK引脚为输出*/
}
/*系统各输入输出口初始化子程序*/
void initial()
{
TRISA=0x00;
TRISD=0x00; /*D口为输出方式*/
INTCON=0x00; /*关闭所有中断*/
}
/*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初始化*/
while(1)
{
SPIIN(); /*SPI接收外部数据*/
SPIOUT(data); /*送出数据显示*/
}
}
5.6.1 CCP模块的PWM工作方式简介
下面给出一个CCP模块设置为PWM操作时的初始化程序
例5.4 CCP模块设置为PWM方式时的初始化程序
/*CCP1模块的PWM工作方式初始化子程序*/
void CCP1INIT()
{
CCPR1L=0X7F;
CCP1CON=0X3C; /*设置CCP1模块为PWM工作方式,且其工作循环
*的低2位为11,高8位为01111111=7F*/
INTCON=0X00; /*禁止总中断和外围中断*/
PR2=0XFF; /*设置PWM的工作周期*/
TRISC=0XFB; /*设置CCP1引脚为输出方式*/
}
该初始化子程序设置CCP1模块输出分辨率为10位的PWM波形,且占空比为50%。
5.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
{
;
}while(1); /*系统开始输出PWM波形。如果系统是
*多任务的,则可以在此执行其它任务,而
*不会影响PWM波形的产生*/
}
5.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,发光二极管灭*/
while(1)
{
;
} /*死循环,等待看门狗溢出复位*/
}
5.8.3 程序清单
该例在PIC16F877休眠前使8个发光二极管的高4个发光,然后进入休眠工作方式;若按键引起的中断将其激活,则低4个发光。用C语言编写程序时,语句SLEEP()相当于汇编语言中的语句“sleep”,使单片机进入休眠状态。
#include <pic.h>
/*该程序实现PIC16F877的休眠工作方式,并由实验板上的按键产生"电平变化中断"将其*从休眠状态中激活。休眠与激活的状态由与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灯亮*/
while(1)
{
;
}
}
6.1 A/D转换的应用
例6.1 A/D转换初始化程序
//A/D转换初始化子程序
void adinitial( )
{
ADCON0 = 0x51; //选择A/D通道为RA2,打开A/D转换器
//在工作状态,且使AD转换时钟为8tosc
ADCON1 = 0X80; //转换结果右移,及ADRESH寄存器的高6位为"0"
//且把RA2口设置为模拟量输入方式
PIE1 = 0X00;
PIE2 = 0X00;
ADIE = 1; //A/D转换中断允许
PEIE = 1; //外围中断允许
TRISA2=1; //设置RA2为输入方式
}
6.1.2 程序清单
下面给出一个调试通过的例程,可作为读者编制程序的参考。该程序中用共用体的方式把A/D转换的10位结果组合在一起。有关共用体的详细资料请参考本书相关章节。
# include <pic.h>
union adres
{int y1;
unsigned char adre[2];
}adresult; //定义一个共用体,用于存放A/D转换的结果
unsigned char i;
unsigned int j;
//系统各I/O口初始化子程序
void initial()
{
TRISD=0X00; //D口为输出
i=0x00;
}
//A/D转化初始化子程序
void adinitial()
{
ADCON0=0x51; //选择A/D通道为RA2,打开A/D转换器
//在工作状态,且使A/D转换时钟为8tosc
ADCON1=0X80; //转换结果右移,及ADRESH寄存器的高6位为"0"
//且把RA2口设置为模拟量输入方式
PIE1=0X00;
PIE2=0X00;
ADIE=1; //A/D转换中断允许
PEIE=1; //外围中断允许
TRISA2=1; //设置RA2为输入方式
}
//延时子程序
void delay()
{
for(j=5535;--j;) continue;
}
//报警子程序
void alarm()
{
i=i^0xFF; //通过异或方式每次把i的各位值取反
PORTD=i; //D口输出i的值
}
//中断服务程序
void interrupt adint(void)
{
ADIF=0; //清除中断标志
adresult.adre[0]=ADRESL;
adresult.adre[1]=ADRESH; //读取并存储A/D转换结果,A/D转换的结果通过共
//用体的形式放入了变量y1中
if(adresult.y1>0x200)
{
alarm(); //如果输入的模拟量大于2.5V(对应数字量
//0X200h),则调用报警子程序
delay(); //调用延时子程序,使电压检测不要过于频繁
}
else PORTD=0XF0 ; //如果输入的模拟量小于2.5V,则与D口相连的
//8个发光二极管的低4个发亮,表示系统正常
ADGO=1; //启动下一次A/D转换
}
//主程序
main()
{
adinitial(); //A/D转换初始化
initial(); //系统各I/O口初始化
ei(); //总中断允许
ADGO=1; //启动A/D转换
while(1)
{
;
} //等待中断,在中断中循环检测外部电压
}
6.2.2 I2C总线工作方式相关子程序
1.C语言编写的I2C总线工作方式的初始化子程序
//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模块使能
}
2.C语言编写的I2C总线工作方式传输数据子程序
需要发送的数据在寄存器j中。
//I2C总线输出数据子程序
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
}
6.2.4程序清单
下面给一个例程。该程序利用MAX518进行D/A转换,且从D/A0引脚输出一个正弦波形。可作为读者编制程序的参考。特别注意,在调试该程序时,把模板上的钮子开关S8拔向高电平,以免发生资源冲突。
#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总线方式送出数据
}
}
7.2.2 程序清单
该源程序已在实验板上调试通过,读者可直接引用,并可利用软件编程的灵活性,加以拓展,实现更为复杂的功能。
#include <pic.h>
#include <math.h>
//此程序实现计时秒表功能,时钟显示范围00.00~99.99秒,分辨度:0.01秒
unsigned char s0,s1,s2,s3;
//定义0.01 秒、0.1 秒、1秒、10秒计时器
unsigned char s[4];
unsigned char k ,data ,sreg;
unsigned int i;
const table[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0XD8,0x80,0x90};
//不带小数点的显示段码表
const table0[10]={0X40,0X79,0X24,0X30,0X19,0X12,0X02,0X78,0X00,0X10};
//带小数点的显示段码表
//TMR0初始化子程序
void tmint()
{
T0CS=0; //TMR0工作于定时器方式
PSA=1; //TMR0不用分频
T0IF=0; //清除TMR0的中断标志
T0IE=1; //TMR0中断允许
}
//spi显示初始化子程序
void SPIINIT()
{
PIR1=0;
SSPCON=0x30;
SSPSTAT=0xC0;
//设置SPI的控制方式,允许SSP方式,并且时钟下降沿发送。与"74HC595,当其
//SCLK从低到高跳变时,串行输入寄存器"的特点相对应
TRISC=0xD7; //SDO引脚为输出,SCK引脚为输出
TRISA5=0; //RA5引脚置为输出,输出显示锁存信号
}
//系统其它部分初始化子程序
void initial()
{
TRISB1=0;
TRISB2=0;
TRISB4=1;
TRISB5=1; //设置与键盘有关的各口的输入输出方式
RB1=0;
RB2=0; //建立键盘扫描的初始条件
}
//SPI传输数据子程序
void SPILED(data)
{
SSPBUF=data; //启动发送
do {
;
}while(SSPIF==0);
SSPIF=0;
}
//显示子程序,显示4位数
void dispaly()
{
RA5=0; //准备锁存
for(k=4;k>0;k--)
{
data=s[k-1];
if(k==3) data=table0[data];//第二位需要显示小数点
else data=table[data];
SPILED(data); //发送显示段码
}
for(k=0;k<4;k++)
{
data=0xFF;
SPILED(data); //连续发送4个DARK,使显示好看一些
}
RA5=1; //最后给锁存信号,代表显示任务完成
}
//软件延时子程序
void DELAY()
{
for(i = 3553; --i ;) continue;
}
//键扫描子程序
void KEYSCAN()
{
while(1){
while(1)
{
dispaly(); //调用一次显示子程序
if ((RB5==0)||(RB4==0)) break;
}
DELAY(); //若有键按下,则软件延时
if ((RB5==0)||(RB4==0)) break;//若还有键按下,则终止循环扫描,返回
}
}
//等键松开子程序
void keyrelax()
{
while(1){
dispaly(); //调用一次显示子程序
if ((RB5==1)&&(RB4==1)) break;
} //为防止按键过于灵敏,每次等键松开才返回
}
//系统赋值初始化子程序
void inizhi()
{
s0=0x00;
s[0]=s0;
s1=0x00;
s[1]=s1;
s2=0x00;
s[2]=s2;
s3=0x00;
s[3]=s3; //s0=s1=s2=s3=0,并放入显示缓冲数组中
sreg=0x00; //tmr0中断次数寄存器清0
}
//中断服务程序
void interrupt clkint(void)
{
TMR0=0X13; //对TMR0写入一个调整值。因为写入TMR0后接着的
//两个周期不能增量,中断需要3个周期的响应时间,
//以及C语言自动进行现场保护要消耗周期
T0IF=0; //清除中断标志
CLRWDT();
sreg=sreg+1; //中断计数器加1
if(sreg==40) //中断次数为40后,才对S0,S1,S2,S3 操作
{
sreg=0;
s0=s0+1;
if(s0==10){
s0=0 ;
s1=s1+1;
if(s1==10){
s1=0 ;
s2=s2+1;
if(s2==10){
s2=0;
s3=s3+1;
if(s3==10) s3=0 ;
}
}
}
}
s[0]=s0;
s[1]=s1;
s[2]=s2;
s[3]=s3;
}
//主程序
main()
{
OPTION=0XFF;
tmint(); //TMR0初始化
SPIINIT(); //spi显示初始化
initial(); //系统其它部分初始化
di(); //总中断禁止
while(1) {
inizhi(); //系统赋值初始化
KEYSCAN(); //键扫描,直到开始键按下
keyrelax(); //等键松开
ei(); //总中断允许
KEYSCAN(); //键扫描直到停止键按下,在键扫描时有显示
keyrelax() ; //等键松开
di(); //总中断禁止
KEYSCAN(); //键扫描到清0键按下,在键扫描时有显示
keyrelax() ; //等键松开
}
}
8.5 单片机双机异步通信
1 单片机PIC1编程(发送部分)
#include <pic.h>
/*该程序实现单片机双机异步通信功能,该程序是发送部分*/
unsigned char tran[8]; /*定义一个数组存储发送数据*/
unsigned char k,data; /*定义通用寄存器*/
const char table[20]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0XD8,0x80,0x90,0x88,0x83, 0xc6,0xa1,0x86,0x8e,0x7f,0xbf,0x89,0xff};
/*不带小数点的显示段码表*/
/*spi显示初始化子程序*/
void SPIINIT()
{
PIR1=0;
SSPCON=0x30;
SSPSTAT=0xC0;
/*设置SPI的控制方式,允许SSP方式,并且时钟下降沿发送,与"74HC595,当其
*SCLK从低到高跳变时,串行输入寄存器"的特点相对应*/
TRISC=0xD7; /*SDO引脚为输出,SCK引脚为输出*/
TRISA5=0; /*RA5引脚设置为输出,以输出显示锁存信号*/
}
/*给数组赋初值子程序 */
void fuzhi()
{
for(k=0;k<8;k++) {
tran[k]=k+3;
}
}
/*SCI部件初始化子程序*/
void sciint()
{
SPBRG=0X19; /*将传输的波特率设为约9 600位/秒*/
TXSTA=0X04; /*选择异步高速方式传输8位数据*/
RCSTA=0X80; /*允许同步串行口工作*/
TRISC6=1;
TRISC7=1; /*将RC6、RC7设置为输入方式,对外部呈高阻状态*/
}
/*SPI传输数据子程序*/
void SPILED(data)
{
SSPBUF=data; /*启动发送*/
do {
;
}while(SSPIF==0);
SSPIF=0;
}
/*显示子程序,显示8位数*/
void display()
{
RA5=0; /*准备锁存*/
for(k=0;k<8;k++) {
data=tran[k];
data=table[data]; /*查得显示的段码*/
SPILED(data); /*发送显示段码*/
}
RA5=1; /*最后给一个锁存信号,代表显示任务完成*/
}
/*主程序*/
main()
{
SPIINIT();
fuzhi(); /*给数组赋初值*/
sciint(); /*SCI部件初始化*/
di(); /*中断禁止*/
TXEN=1; /*发送允许*/
CREN=1; /*接收数据允许*/
for(k=0;k<8;k++){
TXREG=tran[k]; /*发出一个字符*/
while(1){
if(TXIF==1) break;
} /*等待写入完成*/
while(1){
if(RCIF==1) break;/*若收到响应字节,则终止等待*/
}
RCREG=RCREG; /*读响应字节,清RCIF*/
}
display(); /*显示发送的数据*/
while(1){
;
}
}
2 单片机PIC2编程(接收部分)
#include <pic.h>
/*该程序实现单片机双机异步通信功能,该程序是接收部分,并把接收的数据显示在8
*个LED上*/
unsigned char rece[8];/*定义一个数组存储接收数据*/
unsigned char k,data;/*定义通用寄存器*/
const char table[20]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0XD8,0x80,0x90,0x88,0x83, 0xc6,0xa1,0x86,0x8e,0x7f,0xbf,0x89,0xff};
/*不带小数点的显示段码表*/
/*spi显示初始化子程序*/
void SPIINIT()
{
;详细语句见发送程序
}
/*SCI部件初始化子程序*/
void sciint()
{
SPBRG=0X19; /*波特率设置与PIC1相同,为约9 600位/秒*/
TXSTA=0X04; /*异步高速传输*/
RCSTA=0X80; /*串行口工作使能*/
TRISC6=1;
TRISC7=1; /*将RC6、RC7设置为输入方式,对外部呈高阻状态*/
}
/*SPI传送数据子程序*/
void SPILED(data)
{
;详细语句与见发送程序
}
/*显示子程序,显示4位数*/
void display()
{
RA5=0; /*准备锁存*/
for(k=0;k<8;k++){
data=rece[k];
data=table[data]; /*查得显示的段码*/
SPILED(data); /*发送显示段码*/
}
RA5=1; /*最后给一个锁存信号,代表显示任务完成*/
}
/*主程序*/
main()
{
SPIINIT(); /*spi显示初始化*/
sciint(); /*SCI部件初始化*/
di(); /*中断禁止*/
CREN=1; /*接收允许*/
TXEN=1; /*发送允许*/
for(k=0;k<8;k++){
while(1){
if(RCIF==1) break;
} /*等待接收数据*/
rece[k]=RCREG; /*读取接收数据,同时清掉RCIF*/
TXREG=rece[k]; /*发送接收到的数据*/
while(1){
if(TXIF==1) break;
} /*等待写入完成*/
}
display(); /*显示接收的数据*/
while(1){
;
}
}
8.6 单片机双机同步通信
1 单片机PIC1编程(主控发送)
#include <pic.h>
/*该程序实现单片机双机同步通信功能,是主控发送部分。程序上电后显示
*相应的字符,表示系统正常工作。发送完毕后显示发送的数据*/
unsigned char tran[8]; /*定义一个数组存储发送数据*/
unsigned char k,data; /*定义通用寄存器*/
const char table[20]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0XD8,0x80,0x90,0x88,0x83, 0xc6,0xa1,0x86,0x8e,0x7f,0xbf,0x89,0xff};
/*不带小数点的的显示段码表*/
/*spi显示初始化子程序*/
void SPIINIT()
{
;详细程序语句请参考本章8.5节
}
/*给发送数组赋初值子程序 */
void fuzhi()
{
for(k=0;k<8;k++){
tran[k]=k;
} /*发送0~7八个数据*/
}
/*SCI部件初始化子程序*/
void sciint()
{
SPBRG=200 ; /*将传输的波特率设为约9600位/秒*/
TXSTA=0X90; /*选择主控方式*/
RCSTA=0X80; /*允许同步串行口工作*/
TRISC6=1;
TRISC7=1; /*将RC6、RC7设置为输入方式,对外部呈高阻状态*/
}
/*SPI传送数据子程序*/
void SPILED(data)
{
;详细程序语句请参考本章8.5节
}
/*显示子程序,显示8位数*/
void display()
{
RA5=0; /*准备锁存*/
for(k=0;k<8;k++){
data=tran[k];
data=table[data]; /*查得显示的段码*/
SPILED(data); /*发送显示段码*/
}
RA5=1; /*最后给一个锁存信号,代表显示任务完成*/
}
/*显示子程序,显示8位数*/
void display1()
{
RA5=0; /*准备锁存*/
for(k=0;k<8;k++){
data=0xf9; /*显示"1"表示系统正常工作*/
SPILED(data); /*发送显示段码*/
}
RA5=1; /*最后给一个锁存信号,代表显示任务完成*/
}
/*主程序*/
main()
{
SPIINIT(); /*spi显示初始化*/
fuzhi(); /*给发送数组赋发送初值*/
sciint(); /*SCI部件初始化*/
di(); /*中断禁止*/
TXEN=1; /*发送允许*/
display1(); /*显示相应的字符,表示系统正常*/
while(1){
for(k=0;k<8;k++){
TXREG=tran[k];/*发出一个字符*/
while(1){
if(TXIF==1) break;
} /*等待上一个数据写入完成*/
}
display(); /*显示发送的数据*/
} /*循环发送*/
}
2 单片机PIC2编程(从动接收)
#include <pic.h>
/*该程序实现单片机双机 同步通信功能,是从动接收部分,并把接收的数据显
*示在8个LED上*/
unsigned char rece[8]; /*定义一个数组存储接收数据*/
unsigned char k,data; /*定义通用寄存器*/
unsigned int i;
const char table[20]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0XD8,0x80,0x90,0x88,0x83, 0xc6,0xa1,0x86,0x8e,0x7f,0xbf,0x89,0xff};
/*不带小数点的显示段码表*/
/*spi显示初始化子程序*/
void SPIINIT()
{
;详细程序语句请参考本章8.5节
}
/*SCI部件初始化子程序*/
void sciint()
{
TXSTA=0X10 ; /*选择同步从动方式*/
RCSTA=0X90; /*串行口工作使能*/
TRISC6=1;
TRISC7=1; /*将RC6、RC7设置为输入方式对外部呈高阻状态*/
}
/*SPI传送数据子程序*/
void SPILED(data)
{
;/*详细程序语句请参考本章8.5节*/
}
/*显示子程序,显示4位数*/
void display()
{
RA5=0; /*准备锁存*/
for(k=0;k<8;k++){
data=rece[k];
data=table[data]; /*查得显示的段码*/
SPILED(data); /*发送显示段码*/
}
RA5=1; /*最后给一个锁存信号,代表显示任务完成*/
}
/*主程序*/
main()
{
SPIINIT(); /*spi显示初始化*/
sciint(); /*SCI部件初始化*/
di(); /*中断禁止*/
CREN=1; /*接收允许*/
for(k=0;k<8;k++) rece[k]=0x03;
display(); /*显示表示系统正常运行的数据*/
while(1) {
while(1){
CREN=1; /*允许连续接收*/
while(1){
if(RCIF==1) break;
} /*等待接收数据*/
k=0;
rece[k]=RCREG; /*读取接收数据*/
if(OERR==1) { /*如果有溢出错误,则处理*/
CREN=0;
CREN=1;
}
if(rece[k]==0x00) break;/*“0”为同步字符,只有接收到“0”时才进行下面的接收*/
}
for(k=1;k<8;k++){
while(1){
if(RCIF==1) break;
} /*等待接收数据*/
rece[k]=RCREG;/*读取接收数据*/
if(OERR==1) { /*如果有溢出错误,则处理*/
CREN=0;
CREN=1;
}
rece[k]=rece[k]&0x0F;/*屏蔽掉高位,防止干扰*/
}
CREN=0;
display(); /*显示接收的数据*/
for(i=65535;--i; )continue;
for(i=65535;--i; )continue;/*给予一定时间的延时,再进行下一轮接收*/
}
}
8.7 单片机与PC机通信
1 PC机编程
PC采用Toubr C 进行编写。程序如下:
#include<stdio.h>
#define port 0x3f8 /*利用串口1进行通信*/
int ch[15];
main ()
{
int a;
int i,j;
int b[6]={88,15,38,26,20,0};
char c;
clrscr();
outportb(port+3,0x80); /*准备设置波特率*/
outportb(port,0x0C); /*波特率设置为9600bps*/
outportb(port+1,0x00);
outportb(port+3,0x03); /*8位数据,无奇偶检验,1位停止位*/
outportb(port+1,0x00); /*关中断*/
inportb(port+5); /*读一次线路状态寄存器,使其复位*/
for(;;){
printf("\t\tsend data or receive data: (s or r?)\n\n\n");
c=getchar();
switch(c) {
case 's':
case 'S': {
while(!(inportb(port+5)&0x20));/*发送保持器满则等待*/
outportb(port,0x01); /*否则发送数据01,通知单片机准备接收*/
for(i=0;i<6;i++){ /*共发送6个数据*/
a=b[i];
while(!(inportb(port+5)&0x20)) delay(100);/*发送保持器满,等待*/
outportb(port,a); /*发送a*/
printf("%d\n",a); /*显示a*/
while(!(inport(port+5)&1)); /*接收单片机送回的数据*/
ch[i]=inport(port); /*保存*/
}
delay(10);
for(j=0;j<8;j++) printf("\n%d\n",ch[j]);/*显示接收的回送数据*/
getch();
break;
}
case'r': /*接收数据*/
case'R':{
while(!(inportb(port+5)&0x20));
outportb(port,0x02); /*发送数据02,通知单片机发送数据*/
for(j=0;j<9;j++) { /*共接收9个数据*/
while(!(inportb(port+5)&1));
ch[j]=inportb(port);
}
for(j=0;j<9;j++) printf("\n %d\n",ch[j]);
getch();
break;
}
}
}
}
[1] [2] [3] 下一页