单片机课程设计NO.2
程序功能
测量PWM信号脉宽,并用1602显示
程序代码
IIC.C
/*********************IIC.c*************************************/
#include<reg51.h>
#include<IIC.h>
//延时:10us
void I2C_Delay10us()
{
uchar a, b;
for(b=1; b>0; b--)
{
for(a=2; a>0; a--);
}
}
// 起始信号:在I2C_SCL时钟信号在高电平期间I2C_SDA信号产生一个下降沿
void I2C_Start()
{
I2C_SDA = 1;
I2C_Delay10us();
I2C_SCL = 1;
I2C_Delay10us();//建立时间是I2C_SDA保持时间>4.7us
I2C_SDA = 0;
I2C_Delay10us();//保持时间是>4us
I2C_SCL = 0;
I2C_Delay10us();
}
//终止信号:在I2C_SCL时钟信号高电平期间I2C_SDA信号产生一个上升沿
void I2C_Stop()
{
I2C_SDA = 0;
I2C_Delay10us();
I2C_SCL = 1;
I2C_Delay10us();//建立时间大于4.7us
I2C_SDA = 1;
I2C_Delay10us();
}
//通过I2C发送一个字节。在I2C_SCL时钟信号高电平期间, 保持发送信号I2C_SDA保持稳定
uchar I2C_SendByte(uchar dat)
{
uchar a = 0,b = 0;//最大255,一个机器周期为1us,最大延时255us。
for(a=0; a<8; a++)//要发送8位,从最高位开始
{
I2C_SDA = dat >> 7; //起始信号之后I2C_SCL=0,所以可以直接改变I2C_SDA信号
dat = dat << 1;
I2C_Delay10us();
I2C_SCL = 1;
I2C_Delay10us();//建立时间>4.7us
I2C_SCL = 0;
I2C_Delay10us();//时间大于4us
}
I2C_SDA = 1;
I2C_Delay10us();
I2C_SCL = 1;
while(I2C_SDA)//等待应答,也就是等待从设备把I2C_SDA拉低
{
b++;
if(b > 200) //如果超过200us没有应答发送失败,或者为非应答,表示接收结束
{
I2C_SCL = 0;
I2C_Delay10us();
return 0;
}
}
I2C_SCL = 0;
I2C_Delay10us();
return 1;
}
// 使用I2c读取一个字节
uchar I2C_ReadByte(uchar ack)
{
uchar a = 0,dat = 0;
I2C_SDA = 1; //起始和发送一个字节之后I2C_SCL都是0
I2C_Delay10us();
for(a=0; a<8; a++)//接收8个字节
{
I2C_SCL = 1;
I2C_Delay10us();
dat <<= 1;
dat |= I2C_SDA;
I2C_Delay10us();
I2C_SCL = 0;
I2C_Delay10us();
}
if(ack) //回复A
{
I2C_SDA=0;
I2C_SCL = 1;
I2C_Delay10us();
I2C_SCL = 0;
I2C_SDA=1;
I2C_Delay10us();
}
else //回复N
{
I2C_SDA=1;
I2C_SCL = 1;
I2C_Delay10us();
I2C_SCL = 0;
I2C_Delay10us();
}
return dat;
}
//通过IIC读取一个字符串
void ReadString(uchar ads,uchar *s,uchar num) //读取地址(0x00),目的字符串指针,读取个数
{
I2C_Start();
I2C_SendByte(0xa0);
I2C_SendByte(ads);
I2C_Start();
I2C_SendByte(0xa1);
for(;num>0;num--)
*s++=I2C_ReadByte(num);
I2C_Stop(); //读取
}
//发送一个字符串到IIC
void SendString(uchar ads,uchar *s,uchar num)
{
I2C_Start();
I2C_SendByte(0xa0);
I2C_SendByte(ads);
for(;num>0;num--)
{
I2C_SendByte(*s++);
}
I2C_Stop(); //存储
}
IIC.H
/*********************IIC.h*************************************/
#define uchar unsigned char
#define uint unsigned int
//--定义使用的IO口--//
sbit I2C_SCL = P1^3;
sbit I2C_SDA = P1^4;
//--声明全局变量--//
void I2C_Delay10us();
void I2C_Start(); //起始信号:在I2C_SCL时钟信号在高电平期间I2C_SDA信号产生一个下降沿
void I2C_Stop(); //终止信号:在I2C_SCL时钟信号高电平期间I2C_SDA信号产生一个上升沿
uchar I2C_SendByte(uchar dat); //通过I2C发送一个字节。在I2C_SCL时钟信号高电平期间,保持发送信号I2C_SDA保持稳定
uchar I2C_ReadByte(uchar ack); //使用I2c读取一个字节
//通过IIC读取和发送一个字符串
void ReadString(uchar ads,uchar *s,uchar num); //读取地址(0x00),目的字符串指针,读取个数
void SendString(uchar ads,uchar *s,uchar num);
1602.C
/* ---------------------------
LCD1602显示函数
----------------------------*/
#include<reg51.h>
#include<intrins.h>
#include<LCD1602.H>
#define uchar unsigned char
#define uint unsigned int
void delay(uint j) //延时函数
{
uchar i = 250;
for(;j>0;j--)
{
while(--i);
i=249;
while(--i);
i=250;
}
}
void check_busy(void) //检查忙标志函数
{
uchar dt;
do
{
dt = 0xff;
E = 0;
RS = 0;
RW = 1;
E = 1;
dt = out;
}while(dt&0x80);
E = 0;
}
void write_command(uchar com) //写命令函数
{
check_busy();
E = 0;
RS = 0;
RW = 0;
out = com;
E = 1;
_nop_();
E = 0;
delay(1);
}
void write_data(uchar dat) //写显示数据函数
{
check_busy();
E = 0;
RS = 1;
RW = 0;
out = dat;
E = 1;
_nop_();
E = 0;
}
void lcd_initial(void) //LCD初始化函数
{
write_command(0x38); //写入命令0x38:8位两行显示,5*7点阵字符
write_command(0x0c); //写入命令0x0c:开整体显示,光标关,无闪烁
write_command(0x06); //写入命令0x06:光标右移
write_command(0x01); //写入命令0x01:清屏
delay(1);
}
void string (uchar ad,uchar *s) //输出显示字符串的函数
{
write_command(ad);
while(*s>0)
{
write_data(*s++);
}
}
1602.H
/*********************LCD1602.h*************************************/
#define uchar unsigned char
#define uint unsigned int
//--定义使用的IO口--//
#define out P2
sbit RS = P1^5; //LCD1602寄存器选择
sbit RW = P1^6; //LCD1602读写操作选择
sbit E = P1^7; //LCD1602使能信号
//--声明全局变量--//
void lcd_initial(void); //LCD初始化函数
void check_busy(void); //检查忙标志函数
void write_command(uchar com); //写命令函数
void write_data(uchar dat); //写数据函数
void string(uchar ad,uchar *s); //显示字符串函数
void delay(uint j);
主函数
/*
项目名:PWM信号脉宽测量
程序设计思路:
INT0:
1.利用INT0的高电平作为T0计时开启的条件(硬件实现);
2.在INT0中断中读取计时时间;
T0:
用于脉宽计时;
T1:
用于产生PWM信号;
T2:
用于串口通信;
脉宽计每50次测量刷新一次显示值,
*/
//进度:T2计时器无法手动装载,欲将其作为波特率发生器,与T1进行调换
#include<reg52.h>
#include<intrins.h>
#include<string.h>
#include<IIC.H>
#include<LCD1602.H>
#define FOSC 12000000 // 12000000 //System frequency
#define BAUD 9600 //UART baudrate
sbit TEXT = P1^0;
sbit PWM = P1^2;
uchar timeh,timel; //用于提取脉宽计时器T0的数值
uchar GT1H;GT1L;DT1H;DT1L; //定时器T1的预装值:高T1高位,高T1低位,低T1高位,低T1低位
uchar ideal_duty[2]; //理想的占空比,用于:1.接收上位机的占空比 2.发送给C02存储芯片 3.计算T1预装值
//0123456789012345678
uchar mark[]="Duty cycle:",Duty_cycle[]="00.00%",journal[]="100Hz 0000us 00.00% ";
uint pulse_width; //测量得外部信号的脉宽
bit renovate_flag,duty_flag;
void update_T1();
/*串口发送函数*/
void send(uchar *s);
void main( )
{
lcd_initial(); //LCD初始化
T2CON=0X34; //设置T2为波特率发生器,开启T2计时器
TMOD= 0X19;
TL2 = RCAP2L = (65536-(FOSC/32/BAUD)); //Set auto-reload vaule
TH2 = RCAP2H = (65536-(FOSC/32/BAUD)) >> 8;
SCON = 0X50;
EA = 1; //总中断开
ES = 1; //串口中断开
TR0 = 1; //计时器T0开启
ET1 = 1; //T1中断开启
EX0 = 1; //外部中断0开
IT0 = 1; //外部中断0下降沿触发
ReadString(0x40,ideal_duty,2);
update_T1();
TR1 = 1; //T1计时器开启
while(1)
{
if(duty_flag) //刷新输出占空比
{
update_T1();
SendString(0x40,ideal_duty,2);
duty_flag=0;
}
if(renovate_flag) //刷新测量显示数值
{
pulse_width=timel+256*timeh;
Duty_cycle[0]=(pulse_width/1000)%10+'0';
Duty_cycle[1]=(pulse_width/100)%10+'0';
Duty_cycle[3]=(pulse_width/10)%10+'0';
Duty_cycle[4]=pulse_width%10+'0'-1;
write_command(0x01); //清屏
string(0x80,mark);
string(0xc0,Duty_cycle);
strncpy(&journal[6],Duty_cycle,2);
journal[8]=Duty_cycle[3];
journal[9]=Duty_cycle[4];
strcpy(&journal[13],Duty_cycle); //书写日志
TI=1;
renovate_flag=0;
}
}
}
void INT0_Interrupt() interrupt 0 //外部中断0,统计待测信号的脉冲宽度
{
static uchar time=0;
timel=TL0;
timeh=TH0;
TH0=0;
TL0=0;
time++;
if(time==50)
{
time=0;
renovate_flag=1;
}
}
/* ---------------------------
PWM信号生成函数
----------------------------*/
void T1_INT(void) interrupt 3
{
if(PWM==0)
{
PWM = 1;
TH1 = GT1H;
TL1 = GT1L;
}
else
{
PWM = 0;
TH1 = DT1H;
TL1 = DT1L;
}
}
void update_T1()
{
static uchar duty;
static uint temp,freq = 100; //PWM频率为100Hz
duty = (ideal_duty[0]-'0')*10+ideal_duty[1]-'0'; //计算占空比
if(duty<1||duty>99)
{
duty = 20;
}
temp=65535 - FOSC/12/freq*duty/100+12; //高电平持续时间
GT1H=(uchar)(temp>>8);
GT1L=(uchar)temp;
temp=65535 - FOSC/12/freq*(100-duty)/100; //低电平持续时间
DT1H=(uchar)(temp>>8);
DT1L=(uchar)temp;
}
/* ---------------------------
上下位机通信函数
----------------------------*/
void TRX_Interrupt() interrupt 4 //串口通信
{
static temp=0;
if(RI)
{
RI = 0;
if(temp==0)
{
ideal_duty[0]=SBUF;
temp++;
}
else if(temp==1)
{
ideal_duty[1]=SBUF;
temp=0;
duty_flag=1;
}
}
if(TI)
{
TI = 0;
send(journal);
}
}
void send(uchar *s) //串口发送函数
{
static uchar i=0;
if(i<strlen(s))
{
SBUF=*(s+i);
i++;
}
else if(i==strlen(s))
{
SBUF='\n';
i++;
}
else
{
i=0;
}
}