用数字滤波的方式,并在LCD 1602上显示
//////////////////////////////////////////////////
// Author : Toby
// Date : 2009/03/08
// Function : 数字电压表程序,用数字滤波的方式,并在LCD 1602上显示
// MCU : PIC16F877A
// C Compiler : PICC 805
// IDE : MPLAB IDE v810
/////////////////////////////////////////////////
__CONFIG(0x1832);
//芯片配置字,看门狗关,上电延时开,掉电检测关,低压编程关,加密,4M晶体HS振荡
union adres //定义一个共用体,用于存放10位A/D转换的结果
{int y1;
unsigned char adre[2];
}adresult;
unsigned int ADC=0;
void adinitial_voltage(void); // RA0为AD输入
const char TABLE[]={'0','1','2','3','4','5','6','7','8','9'}; //定义常数0-9的数据表格
const char data[4]={'0','0','0','0'};
#define rs RA1
#define rw RA2
#define e RA3
const char voltage[ ]={'V','O','L','T','A','G','E','='};
unsigned char ge=0,shi=0,bai=0,qian=0; //定义6个临时变量
void init(); //申明I/O口初始化函数
void lcd_init(); //申明LCD初始化函数
void lcd_dis();
void write_voltage();
void write(char x); //申明显示1字节数据函数
void lcd_enable(); //申明LCD显示设置函数
void delay(); //申明延时函数
//---------------------------------------
//主函数
void main()
{
unsigned char a;
init(); //调用I/O口初始化函数
while(1)
{
adinitial_voltage();
lcd_init(); //调用LCD初始化函数
for(a=10;a>=1;a--)
{
PORTD=0X80;
lcd_enable();
write_voltage();
PORTD=TABLE[qian]; //待显示数据送PORTD口
lcd_dis();
PORTD=''; //待显示数据送PORTD口
lcd_dis();
PORTD=TABLE[bai]; //待显示数据送PORTD口
lcd_dis();
PORTD=TABLE[shi]; //待显示数据送PORTD口
lcd_dis();
PORTD=TABLE[ge]; //待显示数据送PORTD口
lcd_dis();
}
}
}
//---------------------------------------
//I/O口初始化函数
void init()
{
ADCON1=0X0; //设置A口为普通I/O口
TRISA=0B00000001; //设置A口为输出
TRISD=0X00; //设置D口为输出
}
//---------------------------------------
//LCD初始化函数
void lcd_init()
{
PORTD=0X1; //清除显示
lcd_enable();
PORTD=0X38; //8位2行57点阵
lcd_enable();
PORTD=0X0c; //显示开,光标开,闪烁
lcd_enable();
PORTD=0X06; //文字不动,光标右移
lcd_enable();
}
//--------------------------------------
//显示公司tel函数
void write_voltage()
{
unsigned char i;
for(i=0;i<=7;i++) //一共显示16字节数据
{
write(voltage[i]); //查表获取数据并调用写一个字节数据函数送LCD显示
}
}
//--------------------------------------
//写一个字节数据函数
void write(char x)
{
PORTD=x; //待显示数据送PORTD口
lcd_dis();
}
//--------------------------------------
//LCD显示设置函数
void lcd_enable()
{
rs=0; //该字节数据为命令,而不是数据 RS=1数据RS=0命令
rw=0; //此次 *** 作为写,而不是读 RW=1读RW=0写
e=0; //拉低使能信号
delay(); //保持使能信号为低一段时间
e=1; //拉高使能信号,建立LCD *** 作所需要的上升沿
}
void lcd_dis()
{
rs=1; //该字节数据为命令,而不是数据 RS=1数据RS=0命令
rw=0; //此次 *** 作为写,而不是读 RW=1读RW=0写
e=0; //拉低使能信号
delay(); //保持使能信号为低一段时间
e=1; //拉高使能信号,建立LCD *** 作所需要的上升沿
}
//延时函数
void delay()
{
int i;
for(i=0;i<50;i++);
}
void adinitial_voltage(void) // RA0为AD输入
{
unsigned int AD_data[5]={0,0,0,0,0},j=0,temp=0;
for(j=0;j<=4;++j)
{
ADCON0=0B01000001; //A/D 转换器模块工作;但AD转换不进行;FOSC/8
ADCON1=0B11101110; //ADRESH 寄存器的高6 位读为0;6位与7位须置1
ADGO=1;
while(ADGO); //等AD转换完成
adresultadre[0]=ADRESL;
adresultadre[1]=ADRESH; //读取并存储A/D转换结果
AD_data[j]=adresulty1;
}
for(j=0;j<=4;++j)
if(AD_data[j]>AD_data[j+1])
{temp=AD_data[j];AD_data[j]=AD_data[j+1];AD_data[j+1]=temp;} //找出最大的数,
for(j=0;j<=3;++j)
if(AD_data[j]<AD_data[j+1])
{temp=AD_data[j];AD_data[j]=AD_data[j+1];AD_data[j+1]=temp;} //找出最小数
ADC=((AD_data[0]+AD_data[1]+AD_data[2])/3)/213200083281282531750988965230061000; //中间数作平均
qian=ADC/1000;
bai=ADC%1000/100;
shi=ADC%100/10;
ge=ADC%10;
}#include<pich>//SPI同步串行口的应用(AT93C46为双通信的),往93c46里写654321,再读出来给数码管显示654321
//因为pic里内置了MSSP模块我们只需要设置相应的寄存器即可,不需要像51那样写虚拟时序
#define uchar unsigned char//宏定义
#define uint unsigned int
#define CS RC2//宏定义将CS等同于RC2,如果将引脚用作运算判断等要将其宏定义,否则只能作输出高低电平用
#define DO RC4
//#define DI RC5
#define nop() asm("nop")//如果空指令是小写则是汇编语言的空指令,必须加宏定义并转变成C语言才有效,或者直接大写
__CONFIG(0x3b31);//设置配置位
const uchar table[]={0x3f,0x06,0x5b,0x4f,//注意code是用在51单片机中的程序储存器中,const是一个常量,pic和51的单片机也可以共用的常量,但要写在前头
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71,0x20};//数码管数字表从0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,无显示
uchar data_temp;//先定义一个装数据的变量
void delay(uint x);//声明
void init();
void didi(uchar);
void disp(uchar num1,uchar num2,uchar num3,uchar num4,uchar num5,uchar num6);
uchar read(uchar add);
void write(uchar add,uchar wdata);
void comm(uchar data);
void write_en();
void main()
{
uchar a1,a2,a3,a4,a5,a6,i;//定义6个数码管显示的变量,定义一个将6个数码管显示的数循环写进去又循环读出来的i变量
init();//调用初始化
write_en();//调用写允许
for(i=6;i>0;i--)//因为有6个数码管
{
write(i,i);//因为要显示654321,第一次显示的是i=6,往里面写i=6的地址,再写显示i=6的数据
}//这样用for循环将654321依次写入相对应的地址和数据
a1=read(6);//将93c46里面的相对应的地址读出来显示, 读6的地址,然后返回数据
a2=read(5);
a3=read(4);
a4=read(3);
a5=read(2);
a6=read(1);//因为没有大10的数不用分离
while(1)//因为要不断地循环扫描键盘检测是否按下所以要进行死循环
{
disp(a1,a2,a3,a4,a5,a6);//在调用数码管动态扫描程序
}
}
void delay(uint x)//延迟函数x表示毫秒
{
uint a,b;
for(a=x;a>0;a--)
for(b=110;b>0;b--);//嵌套
}
void init()//因为不用到定时器所以不需要设置定时器寄存器
{
TRISD=0;//设置数码管段选为输出状态
TRISA=0;//设置数码管位选为输出状态
TRISC=0x10;//0001 0000只有RC4为输入,其他为输出状态,因为SPI通信时ss,sck,sdi,sdo就算不用到也都要设置,其中RC2是AT93C46的片选
TRISE0=0;//设置蜂鸣器RE0为输出状态
SSPSTAT=0x80;//设置状态寄存器,设置SSPE置1为ss,sck,sdi,sdo四个端口为SPI通信
SSPCON=0x32;//设置控制寄存器
CS=0;//初始化时不能选通片选
}
void write(uchar add,uchar wdata)//往93c46写地址+写数据的指令
{
CS=1;//片选先置高电平
nop();//延时
comm(0x02);//调用往93c46写发送数据的指令,0x02是写 *** 作的开始及第一个 *** 作码,第二个加入下一条7位地址组成8位
//因为是8位的寄存器所以里面的数不能超过8位,否则要分开两次或多次写
comm(add|0x80);//因为只有7个地址加第二个 *** 作码是1:xxx xxxx|1000 0000,得:1xxx xxxx组成了8位
comm(wdata);//写完地址后紧跟着要写数据
CS=0;//写完地址和数据后片选置低
nop();nop();
CS=1;//给DO知道此为发送完毕不忙了,DO则会自动置为1
while(!DO);//发送完毕后DO置1再取反为假继续执行下一步
CS=0;//释放片选
}
void write_en()//写允许/禁止的指令
{
CS=1;//打开片选
nop();
comm(0x02);//写允许的指令第一个是开始位1,加 *** 作码的第一个1
comm(0x60);//0110 0000 8位时11x xxxx可以填补0代替
CS=0;//释放片选
}
void comm(uchar data)//往93c46里写东西的指令
{
SSPBUF=data;//将数据写入SSPBUF(这时会自动发送写入)。SSPBUF是和51的SBUF一样是一个特殊功能的寄存器
while(!SSPIF);//检测低外设中断标志位是否自动置1,置1则说明发送完毕,再取反为零为假继续执行下一步
SSPIF=0;//置位后清零
data_temp=SSPBUF;//将sspbuf里的数据给data_temp取走
}
uchar read(uchar add)//往93c46读取的指令
{
uchar rtemp;//定义返回的数据的变量
CS=1;
nop();
comm(0x03);//发送读取的指令:开始位1+ *** 作码10,因为第二个 *** 作码是0所以下一条不用或运算组成8位
comm(add);//要读取的地址
comm(0);//随便往93c46里发送一个0
CS=0;//读完要释放片选
rtemp=data_temp;//从将sspbuf里的数据给data_temp取走后再附给rtemp
return (rtemp);//返回数据,因为读取的是地址,目的是要地址里的数据,所以读地址而返回数据
}
void didi(uchar num)//蜂鸣器程序,num表示响多少声
{
uchar di_num;
for(di_num=num;di_num>0;di_num--)
{
RE0=1;//蜂鸣器响
delay(100);//响100ms
RE0=0;//蜂鸣器关闭
delay(50);//关闭50ms
}
}
void disp(uchar num1,uchar num2,uchar num3,uchar num4,uchar num5,uchar num6)//数码管的扫描函数,要在里面有6个变量,每一个为一个数码管显示的数
{
PORTD=table[num1];//调用数码管的显示函数(注第一个是显示0)这是第一个数码管要显示的段选
PORTA=0x20;//00100000由原理图可得第一个数码管是由RA5控制位选的
delay(10);//因为是要动态,所以要加延时,但时间不能太长
PORTD=table[num2];//调用数码管的显示函数(注第一个是显示0)这是第二个数码管要显示的段选
PORTA=0x10;//00010000由原理图可得第二个数码管是由RA4控制位选的
delay(10);//因为是要动态,所以要加延时,但时间不能太长
PORTD=table[num3];//调用数码管的显示函数(注第一个是显示0)这是第三个数码管要显示的段选
PORTA=0x08;//00001000由原理图可得第三个数码管是由RA3控制位选的
delay(10);//因为是要动态,所以要加延时,但时间不能太长
PORTD=table[num4];//调用数码管的显示函数(注第一个是显示0)这是第四个数码管要显示的段选
PORTA=0x04;//00000100由原理图可得第四个数码管是由RA2控制位选的
delay(10);//因为是要动态,所以要加延时,但时间不能太长
PORTD=table[num5];//调用数码管的显示函数(注第一个是显示0)这是第五个数码管要显示的段选
PORTA=0x02;//00000010由原理图可得第五个数码管是由RA1控制位选的
delay(10);//因为是要动态,所以要加延时,但时间不能太长
PORTD=table[num6];//调用数码管的显示函数(注第一个是显示0)这是第六个数码管要显示的段选
PORTA=0x01;//00000001由原理图可得第六个数码管是由RA0控制位选的
delay(10);//因为是要动态,所以要加延时,但时间不能太长
}我看你那两个io口的时序好像不大对劲。我给你个代码,直接保存成xxxh文件。插到你代码里面就可以了。
因为我不知道你pd6 和pd7 各接什么脚
你要自行修改以下两句代码
#define SDA 0X02 //定义sda连接的是portd1
#define SCL 0X01 //定义scl连接的是portd0
写数据的时候就调用 write_eep(地址,数据);
读数据的时候就调用 read_eep(地址);
文件内容如下
#define EEP_PORT PORTD //定义是使用PROTC口
#define EEP_DDR DDRD //定义是使用PROTC口
#define SDA 0X02 //定义sda连接的是portd1
#define SCL 0X01 //定义scl连接的是portd0
#define WP 0X04 //定义wp连接的是portd2
#define EEP_ADD_R 0xa1 //写I2C从器件地址和读方式 a0,a1接地
#define EEP_ADD_W 0xa0 //写I2C从器件地址和写方式 a0,a1接地
void eep_init(void)
{
EEP_DDR|= SDA | SCL | WP ;
EEP_PORT|=SDA | SCL | WP ;
}
void send(void)
{
EEP_PORT|=SCL;
asm("nop");
EEP_PORT&=~SCL;
}
/
内部函数,I2C开始
/
void Start(void)
{
EEP_PORT|=SDA; //sda
EEP_PORT|=SCL; //scl
asm("nop");
EEP_PORT&=~SDA;
asm("nop");
EEP_PORT&=~SCL;
}
/
内部函数,I2C结束
/
void Stop(void)
{
EEP_PORT&=~SDA;
EEP_PORT&=~SCL;
asm("nop");
EEP_PORT|=SCL;
asm("nop");
EEP_PORT|=SDA;
asm("nop");
}
/
内部函数,等待ACK
/
void ACK(void)
{
unsigned char errtime=20;
EEP_PORT|=SDA; //上拉
EEP_DDR&=~SDA; //设置为输入口
EEP_PORT|=SCL;
asm("nop");
while(PINC&SDA)
{ errtime--;
if(!errtime)
{
Stop();
EEP_PORT&=~SDA; //超时,给他个离开的理由
}
}
EEP_PORT&=~SCL;
asm("nop");
EEP_DDR|=SDA; //设置为输出口
}
/
内部函数输出数据字节
/
void writebyte(unsigned char wdata)
{
unsigned char i;
for(i=0;i<8;i++)
{
if((wdata&0x80)){EEP_PORT|=SDA;}else{EEP_PORT&=~SDA;}
wdata<<=1;
send(); //发送一个位
}
}
/
内部函数输入数据
/
unsigned char Readbyte(void)
{
unsigned char i,bytedata;
EEP_DDR&=~SDA; //设置为输入
for(i=0;i<8;i++)
{
EEP_PORT|=SCL;
bytedata<<=1;
if(PINC&SDA){
bytedata|=0X01;
}else{
bytedata&=~0X01;
}
EEP_PORT&=~SCL;
asm("nop");
}
EEP_DDR|=SDA;
return(bytedata);
}
/读24cxx--外部调用/
unsigned char read_eep(unsigned char add1,unsigned char add2)
{ unsigned char mdata;
Start(); //起始位
writebyte(EEP_ADD_W); //写eeprom地址+1个写命令
ACK(); //等待应答信号
writebyte(add1); //写如访问地址
ACK(); //等待应答信号
writebyte(add2); //写入访问地址
ACK(); //等待应答信号
Start(); //起始位
writebyte(EEP_ADD_R); //写eeprom地址+1个读命令
ACK(); //等待应答信号
mdata=Readbyte(); //获取数据
Stop(); //发送停止位置
asm("nop"); //此处延时根据实际情况,如果不延时,可能会不正常
return(mdata);
}
/写24cxx--外部调用/
void write_eep(unsigned char add1,unsigned char add2,unsigned char udata)
{
EEP_PORT&=~WP; //关闭写保护
Start(); //起始位
writebyte(EEP_ADD_W); //写eeprom地址+1个写命令
ACK(); //等待应答信号
writebyte(add1); //写如访问地址
ACK(); //等待应答信号
writebyte(add2); //写如访问地址
ACK(); //等待应答信号
writebyte(udata); //写入数据
ACK(); //等待应答信号
Stop(); //发送停止位
delay_nms(4); //此处延时根据实际情况,大多EEPROM都需要3毫秒的写入时间,如果不延时,可能会不正常
EEP_PORT|=WP; //打开写保护
}
//用定时器咯。。设置一下一秒的溢出中断,用动态显示呗。
这是我MEGA16的秒表程序。。差不多吧。。
#include<iom16vh>
#include<macrosh>
#pragma interrupt_handler miao:9
const table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d, 0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
unsigned char num=0;
void delay(unsigned int ms)
{
unsigned int i,j;
for(i=0;i<ms;i++)
{
for(j=0;j<1141;j++);
}
}
void miao()
{
if(num==60)
{
num=0;
}
else
{
num++;
}
TCNT1H=0xc2;
TCNT1L=0xf6;
}
void show(unsigned char j,unsigned char k)
{
PORTD|=BIT(7);
PORTC=table[j];
PORTD&=~BIT(7);
PORTC=0xff;
PORTC&=~BIT(k);
PORTD|=BIT(6);
PORTD&=~BIT(6);
PORTC=0X00;
delay(1);
}
void main()
{
SREG|=BIT(7);
TCCR1B=0x04;
TCNT1H=0xc2;
TCNT1L=0xf6;
TIMSK|=BIT(2);
DDRD=0xc0;
DDRC=0xff;
while(1)
{
show((num/10),0);
delay(1);
show((num%10),1);
delay(1);
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)