2019-04-13 21:47:06 qq_32945473 阅读数 1700
  • 定时器和计数-第1季第10部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第10个课程,主要内容是51单片机的定时器和计数器,本课程的学习目标是对定时器的作用和意义有深入理解,掌握通过操作寄存器来操作硬件的思路和方法。

    1963 人正在学习 去看看 朱有鹏

单片机利用定时器中断制作秒表

#include
#define LED P0

sbit LA=P2^4; //对应着138译码器C,B,A端口
sbit LB=P2^3;
sbit LC=P2^2;

sbit start=P3^1;//三个键控制开关和暂停
sbit pulse=P3^0;
sbit end=P3^2;

char count; //中断次数定义
unsigned  int sec; //秒数
char i0,i1,i2,i3; //led数码管的0~4位
unsigned char Display[10]= {0x3f,0x06,0x5b,0x4f,0x66,
                            0x6d,0x7d,0x07,0x7f,0x6f}; //led数码管段码
void show(char j0,char j1,char j2,char j3); //显示秒数的函数
void TransfromData(unsigned int sec); //数据处理,将秒数转换为个十百千万
void Delay10ms(); //延时函数
void main() 
{
	EA=1; //打开总中断
	ET0=1;//打开定时器中断0
	TMOD=0x01;//选择工作模式1
	TH0=(65536-50000)/256;//定时50ms需要的高位初始化
	TL0=(65536-50000)%6;//定时50ms需要的低位初始化    
  	while(1)
  	{
		show(0,0,0,0);//未开始时显示0000

    	if(start==0 )
    	{
			Delay10ms();//延时消抖
   			if(start==0)
  			{
				TR0=1; //开始计数
				while(pulse!=0 && end!=0) //未按下pulse或者时显示计时
				{
					TransfromData(sec);
					show(i0,i1,i2,i3);
    			}
   
    			if(end==0)//按下end后的操作
				{
					TR0=0;
					TH0=(65536-50000)/256;
      				TL0=(65536-50000)%6;
					sec=0;
					count=0;
					//未下start的操作,一旦按下start可以跳出循环,由于按下按键有几十毫秒的时间
					//在程序开始处仍然可以将start认为是按下的,又开始继续工作
					while(start != 0)
					{ 
    					show(0,0,0,0);
					}
				}
    			if(pulse==0)//按下pulse后的操作
    			{
    				TR0=0;
					while(start != 0)
					{
						TransfromData(sec);
    					show(i0,i1,i2,i3);
					}
				TR0=1;
				}
			} 
		}
	}
}
void TransfromData(unsigned int s) {
	i3=(s/1000);
	i2=(s-i3*1000)/100;
	i1=(s-i3*1000-100*i2)/10;
	i0=s;
}

void int_T0() interrupt 1 {

	TH0=(65536-50000)/256;
	TL0=(65536-50000)%6;
	count++;
	if(count==20) 
	{
		sec++;
		count=0;
	}
}
void show(char j0,char j1,char j2,char j3)
{
	char i;
	char j;
	for(i=0; i<4; i++) 
	{
		switch(i) //通过switch语句进行段选和位选
		{
			case(0):LA=0;LB=0;LC=0;LED=Display[j0];break;
			case(1):LA=0;LB=0;LC=1;LED=Display[j1];break;
   			case(2):LA=0;LB=1;LC=0;LED=Display[j2];break;
			case(3):LA=0;LB=1;LC=1;LED=Display[j3];break;
		}
		j=10;while(j--) ;
		LED=0x00;
	}	
}
void Delay10ms()
{
	unsigned char i, j;
	i = 108;
	j = 145;
	do
	{
		while (--j);
	} 	while (--i);
}
2014-03-01 19:34:57 sxhexin 阅读数 1477
  • 定时器和计数-第1季第10部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第10个课程,主要内容是51单片机的定时器和计数器,本课程的学习目标是对定时器的作用和意义有深入理解,掌握通过操作寄存器来操作硬件的思路和方法。

    1963 人正在学习 去看看 朱有鹏

【2014.1.12】

拍下51开发板及4位8段液晶屏,准备在过年期间完成定时及显示部分的开发。

PS:实际上过年期间什么都没做,玩过去了……

【2014.2.7】

开始制作,暂定为STC12C5A60S2+水银开关+2位数码管组合。水银开关未在本地买到,准备网购,先写定时器和显示部分的程序。

使用水银开关进行位置检测,更改定时时间,以分为单位在数码管上显示。

不定期更新日志。

【2014.2.9】

51语句复习完毕,开始编写程序。

【2014.2.10】

显示部分程序编写完毕,开始学习定时器。

【2014.2.11】

定时器部分编写完毕,计时误差较大。

视频地址:http://v.youku.com/v_show/id_XNjgyNTQ0MTQ0.html

提示音部分完成,工作正常。

【2014.2.12】

与定时精度作斗争,尝试使用外置晶振。

【2014.2.13】

数码管部分段码显示不正常,无存货,暂时使用led替代。

拍下水银开关,等待到货中。

【2014.2.16】

返校&开学,以后使用周末时间进行制作。

【2014.2.19】

历经磨难水银开关到货。

【2014.2.22】小休请假回家

使用水银开关进行位置检测误差较大。

元件堆中发现两片STC15F204EA,尝试移植软件。

【2014.2.23】

凌晨睡不着,开机编程,尝试软件去抖动。

购买到数码管及STC12C5A60S2,软件去抖动无果。

开发板板载单片机不工作,疑似烧毁,崩溃中。

使用STC15F204EA,数码管显示乱码,疑似端口工作方式设置错误,定时器可正常工作

拍下STC下载器、ADXL345、L3G4200D、HMC5883L及IAP15F2K61S2。

【2014.2.28】

元件到货,到家已晚,睡觉。

【2014.3.1】

修复数码管显示问题。

放弃使用L3G4200D和HMC5883L,可能会在下一制作中使用。

改为STC15F204EA+ADXL345+2位数码管组合,使用ADXL345检测X、Y轴的角度。

学习I2C及SPI通讯,尝试驱动ADXL345。

【2014.3.8】

成功驱动ADXL345并显示角度。

开始进行调整定时时间函数的编写。

视频地址:http://v.youku.com/v_show/id_XNjgyNTQzNzg4.html

【2014.3.9】

调整定时时间函数编写完毕。

至此使用的所有函数

void init_io (void);//初始化端口
void init_ADXL345(void);//初始化ADXL345
void ADXL345_Start(void);//开始信号
void ADXL345_Stop(void);//停止信号
void ADXL345_SendACK(bit ack);//发送应答信号 入口参数:ack (0:ACK 1:NAK)
bit ADXL345_RecvACK(void);//接收应答信号
void ADXL345_SendByte(unsigned char dat);//向IIC总线发送一个字节数据
unsigned char ADXL345_RecvByte(void);//从IIC总线接收一个字节数据
void ADXL345_Single_Write(unsigned char REG_Address,unsigned char REG_data);//单字节写入
unsigned char ADXL345_Single_Read(unsigned char REG_Address);//单字节读取
void ADXL345_Multiple_read(void);//连续读出ADXL345内部加速度数据,地址范围0x32~0x37
void calculates_the_angle(void);//计算角度
void test_direction(void);//检测方向
void change_min (void);//根据方向调时间
int num_test (int num);//检测数据是否超出显示范围
void timer_start (void);//打开定时器0
void delay_us(int a);//微秒延时 用于1T单片机
void delay_ms(int a);//毫秒延时 用于1T单片机
void dis_data (unsigned int num);//显示数据
void beep (int x);//打开蜂鸣器

10:30所有功能实现完毕,进入测试阶段。

视频地址:http://v.youku.com/v_show/id_XNjgyNzU1Nzc2.html

在洞洞板上把电路焊出来了,不用看到面包板上那些恼人的飞线了……

准备画PCB。

明天拿到班里测试。

【2014.10.2】

#include "STC15F104E.H"
#include  <math.h>
#include  <stdio.h>	
#include  <INTRINS.H>
/**************************************************************************************
Êý¾ÝÊֲ᣺
http://www.analog.com/zh/mems-sensors/mems-inertial-sensors/adxl345/products/product.html
http://www.stcmcu.com/datasheet/stc/STC-AD-PDF/STC15F204EA-series-chinese.pdf
**************************************************************************************/
void init_io (void);//³õʼ»¯¶Ë¿Ú
void init_ADXL345(void);//³õʼ»¯ADXL345
void ADXL345_Start(void);//¿ªÊ¼ÐźÅ
void ADXL345_Stop(void);//Í£Ö¹ÐźÅ
void ADXL345_SendACK(bit ack);//·¢ËÍÓ¦´ðÐźŠÈë¿Ú²ÎÊý:ack (0:ACK 1:NAK)
bit ADXL345_RecvACK(void);//½ÓÊÕÓ¦´ðÐźÅ
void ADXL345_SendByte(unsigned char dat);//ÏòIIC×ÜÏß·¢ËÍÒ»¸ö×Ö½ÚÊý¾Ý
unsigned char ADXL345_RecvByte(void);//´ÓIIC×ÜÏß½ÓÊÕÒ»¸ö×Ö½ÚÊý¾Ý
void ADXL345_Single_Write(unsigned char REG_Address,unsigned char REG_data);//µ¥×Ö½ÚдÈë
unsigned char ADXL345_Single_Read(unsigned char REG_Address);//µ¥×Ö½Ú¶ÁÈ¡
void ADXL345_Multiple_read(void);//Á¬Ðø¶Á³öADXL345ÄÚ²¿¼ÓËÙ¶ÈÊý¾Ý£¬µØÖ··¶Î§0x32~0x37
void calculates_the_angle(void);//¼ÆËã½Ç¶È
void test_direction(void);//¼ì²â·½Ïò
void change_min (void);//¸ù¾Ý·½Ïòµ÷ʱ¼ä
int num_test (int num);//¼ì²âÊý¾ÝÊÇ·ñ³¬³öÏÔʾ·¶Î§
void timer_start (void);//´ò¿ª¶¨Ê±Æ÷0
void delay_us(int a);//΢ÃëÑÓʱ ÓÃÓÚ1Tµ¥Æ¬»ú
void delay_ms(int a);//ºÁÃëÑÓʱ ÓÃÓÚ1Tµ¥Æ¬»ú
void dis_data (unsigned int num);//ÏÔʾÊý¾Ý
void beep (int x);//´ò¿ª·äÃùÆ÷
/*************************************************************************************/
#define LED P1
#define	SlaveAddress   0xA6	//¶¨ÒåÆ÷¼þÔÚIIC×ÜÏßÖеĴӵØÖ·,¸ù¾ÝALT_ADDRESSÒý½Å²»Í¬ÐÞ¸Ä
							//ALT_ADDRESSÒý½Å½ÓµØʱµØַΪ0xA6£¬½ÓµçԴʱµØַΪ0x3A
							//GY-291Ä£¿éALT_ADDRESSÒý½Åͨ¹ý4.7kR½ÓµØ
/*************************************************************************************/
sbit START = P2^4;
sbit BEEP = P2^5;
sbit LED1 = P2^6;
sbit LED2 = P2^7;
sbit SCL=P2^0;
sbit SDA=P2^1;
/*************************************************************************************/
unsigned char code disdata[10]= {0xcf,0x03,0x5d,0x5b,0x93,0xda,0xde,0x43,0xdf,0xdb};
unsigned char BUF[8];//ADXL345_Multiple_readº¯ÊýÊý¾Ý»º´æ
float Roll,Pitch,Q,T,K;
int i=0,min=25,key=0;
int data_xyz[3];
/*************************************************************************************/
void main (void)
{	
	init_io ();

	init_ADXL345();
	delay_ms(50);
	while(1)
	{
		test_direction();
		change_min();
		dis_data(min);
		if (START == 1)
		{
			beep(1);
			break;
		}						
	}
	timer_start();	
	while (1)
	{
		
		if (min==0)
		{
			TR0=0;
			beep(3);
			return;	
		}
		else
			dis_data(min);			
	}			
}
/*************************************************************************************/
void init_io (void)//³õʼ»¯¶Ë¿Ú
{
    P1M1 = 0x00;
    P1M0 = 0xff;	
	P1 = 0x00;
	P2 = 0x00;
	BEEP = 1;
}
/*************************************************************************************/
void init_ADXL345(void)//³õʼ»¯ADXL345
{
   ADXL345_Single_Write(0x31,0x0B);   //²âÁ¿·¶Î§,Õý¸º16g£¬13λģʽ
   ADXL345_Single_Write(0x2C,0x08);   //ËÙÂÊÉ趨Ϊ12.5 ²Î¿¼pdf13Ò³
   ADXL345_Single_Write(0x2D,0x08);   //Ñ¡ÔñµçԴģʽ   ²Î¿¼pdf24Ò³
   ADXL345_Single_Write(0x2E,0x80);   //ʹÄÜ DATA_READY ÖжÏ
   ADXL345_Single_Write(0x1E,0x00);   //X Æ«ÒÆÁ¿ ¸ù¾Ý²âÊÔ´«¸ÐÆ÷µÄ״̬дÈëpdf29Ò³
   ADXL345_Single_Write(0x1F,0x00);   //Y Æ«ÒÆÁ¿ ¸ù¾Ý²âÊÔ´«¸ÐÆ÷µÄ״̬дÈëpdf29Ò³
   ADXL345_Single_Write(0x20,0x05);   //Z Æ«ÒÆÁ¿ ¸ù¾Ý²âÊÔ´«¸ÐÆ÷µÄ״̬дÈëpdf29Ò³
}
/*************************************************************************************/
void ADXL345_Start(void)//¿ªÊ¼ÐźÅ
{
    SDA = 1;                    //À­¸ßÊý¾ÝÏß
    SCL = 1;                    //À­¸ßʱÖÓÏß
    delay_us(50);                 //ÑÓʱ
    SDA = 0;                    //²úÉúϽµÑØ
    delay_us(50);                 //ÑÓʱ
    SCL = 0;                    //À­µÍʱÖÓÏß
}

/*************************************************************************************/
void ADXL345_Stop(void)//Í£Ö¹ÐźÅ
{
    SDA = 0;                    //À­µÍÊý¾ÝÏß
    SCL = 1;                    //À­¸ßʱÖÓÏß
    delay_us(5);                 //ÑÓʱ
    SDA = 1;                    //²úÉúÉÏÉýÑØ
    delay_us(5);                 //ÑÓʱ
}

/*************************************************************************************/
void ADXL345_SendACK(bit ack)//·¢ËÍÓ¦´ðÐźŠÈë¿Ú²ÎÊý:ack (0:ACK 1:NAK)
{
    SDA = ack;                  //дӦ´ðÐźÅ
    SCL = 1;                    //À­¸ßʱÖÓÏß
    delay_us(5);                 //ÑÓʱ
    SCL = 0;                    //À­µÍʱÖÓÏß
    delay_us(5);                 //ÑÓʱ
}

/*************************************************************************************/
bit ADXL345_RecvACK(void)//½ÓÊÕÓ¦´ðÐźÅ
{
    SCL = 1;                    //À­¸ßʱÖÓÏß
    delay_us(5);                 //ÑÓʱ
    CY = SDA;                   //¶ÁÓ¦´ðÐźÅ
    SCL = 0;                    //À­µÍʱÖÓÏß
    delay_us(5);                 //ÑÓʱ

    return CY;
}

/*************************************************************************************/
void ADXL345_SendByte(unsigned char dat)//ÏòIIC×ÜÏß·¢ËÍÒ»¸ö×Ö½ÚÊý¾Ý
{
    unsigned char i;
    for (i=0; i<8; i++)         //8λ¼ÆÊýÆ÷
    {
        dat <<= 1;              //ÒƳöÊý¾ÝµÄ×î¸ßλ
        SDA = CY;               //ËÍÊý¾Ý¿Ú
        SCL = 1;                //À­¸ßʱÖÓÏß
        delay_us(5);             //ÑÓʱ
        SCL = 0;                //À­µÍʱÖÓÏß
        delay_us(5);             //ÑÓʱ
    }
    ADXL345_RecvACK();
}

/*************************************************************************************/
unsigned char ADXL345_RecvByte(void)//´ÓIIC×ÜÏß½ÓÊÕÒ»¸ö×Ö½ÚÊý¾Ý
{
    unsigned char i;
    unsigned char dat = 0;

    SDA = 1;                    //ʹÄÜÄÚ²¿ÉÏÀ­,×¼±¸¶ÁÈ¡Êý¾Ý,
    for (i=0; i<8; i++)         //8λ¼ÆÊýÆ÷
    {
        dat <<= 1;
        SCL = 1;                //À­¸ßʱÖÓÏß
        delay_us(5);             //ÑÓʱ
        dat |= SDA;             //¶ÁÊý¾Ý               
        SCL = 0;                //À­µÍʱÖÓÏß
        delay_us(5);             //ÑÓʱ
    }
    return dat;
}

/*************************************************************************************/
void ADXL345_Single_Write(unsigned char REG_Address,unsigned char REG_data)//µ¥×Ö½ÚдÈë
{
    ADXL345_Start();                  //ÆðʼÐźÅ
    ADXL345_SendByte(SlaveAddress);   //·¢ËÍÉ豸µØÖ·+дÐźÅ
    ADXL345_SendByte(REG_Address);    //ÄÚ²¿¼Ä´æÆ÷µØÖ·£¬Çë²Î¿¼ÖÐÎÄpdf22Ò³ 
    ADXL345_SendByte(REG_data);       //ÄÚ²¿¼Ä´æÆ÷Êý¾Ý£¬Çë²Î¿¼ÖÐÎÄpdf22Ò³ 
    ADXL345_Stop();                   //·¢ËÍÍ£Ö¹ÐźÅ
}

/*************************************************************************************/
unsigned char ADXL345_Single_Read(unsigned char REG_Address)//µ¥×Ö½Ú¶ÁÈ¡
{  unsigned char REG_data;
    ADXL345_Start();                          //ÆðʼÐźÅ
    ADXL345_SendByte(SlaveAddress);           //·¢ËÍÉ豸µØÖ·+дÐźÅ
    ADXL345_SendByte(REG_Address);            //·¢ËÍ´æ´¢µ¥ÔªµØÖ·£¬´Ó0¿ªÊ¼	
    ADXL345_Start();                          //ÆðʼÐźÅ
    ADXL345_SendByte(SlaveAddress+1);         //·¢ËÍÉ豸µØÖ·+¶ÁÐźÅ
    REG_data=ADXL345_RecvByte();              //¶Á³ö¼Ä´æÆ÷Êý¾Ý
	ADXL345_SendACK(1);   
	ADXL345_Stop();                           //Í£Ö¹ÐźÅ
    return REG_data; 
}
/*************************************************************************************/
void ADXL345_Multiple_read(void)//Á¬Ðø¶Á³öADXL345ÄÚ²¿¼ÓËÙ¶ÈÊý¾Ý£¬µØÖ··¶Î§0x32~0x37
{   unsigned char i;
    ADXL345_Start();                          //ÆðʼÐźÅ
    ADXL345_SendByte(SlaveAddress);           //·¢ËÍÉ豸µØÖ·+дÐźÅ
    ADXL345_SendByte(0x32);                   //·¢ËÍ´æ´¢µ¥ÔªµØÖ·£¬´Ó0x32¿ªÊ¼	
    ADXL345_Start();                          //ÆðʼÐźÅ
    ADXL345_SendByte(SlaveAddress+1);         //·¢ËÍÉ豸µØÖ·+¶ÁÐźÅ
	 for (i=0; i<6; i++)                      //Á¬Ðø¶ÁÈ¡6¸öµØÖ·Êý¾Ý£¬´æ´¢ÖÐBUF
    {
        BUF[i] = ADXL345_RecvByte();          //BUF[0]´æ´¢0x32µØÖ·ÖеÄÊý¾Ý
        if (i == 5)
        {
           ADXL345_SendACK(1);                //×îºóÒ»¸öÊý¾ÝÐèÒª»ØNOACK
        }
        else
        {
          ADXL345_SendACK(0);                //»ØÓ¦ACK
       }
   }
    ADXL345_Stop();                          //Í£Ö¹ÐźÅ
    delay_ms(5);
}
/*************************************************************************************/
void calculates_the_angle(void)//¼ÆËã½Ç¶È
{
	data_xyz[0]=(BUF[1]<<8)+BUF[0];//XÖáÊý¾Ý
	data_xyz[1]=(BUF[3]<<8)+BUF[2];//YÖáÊý¾Ý   
	data_xyz[2]=(BUF[5]<<8)+BUF[4];//ZÖáÊý¾Ý
	Q=(float)data_xyz[0]*3.9;
	T=(float)data_xyz[1]*3.9;
	K=(float)data_xyz[2]*3.9;
	Q=-Q;
	Roll=(float)(((atan2(K,Q)*180)/3.14159265)+180);//XÖá½Ç¶ÈÖµ
	Pitch=(float)(((atan2(K,T)*180)/3.14159265)+180);//YÖá½Ç¶ÈÖµ
}
/*************************************************************************************/
void test_direction(void)//¼ì²â·½Ïò
{
	ADXL345_Multiple_read();
	calculates_the_angle();
	Pitch=(int)Pitch;
	Roll=(int)Roll;
	if (Pitch>180&&Pitch<240&&Roll>225&&Roll<315)
	{
		if (Pitch<225)
			key=1;
		else
			key=5;
	}	
	else if (Pitch>300&&Pitch<360&&Roll>225&&Roll<315)
	{
		if (Pitch>315)
			key=2;
		else
			key=6;
	}	
	else if (Pitch>225&&Pitch<315&&Roll>300&&Roll<360)
	{
		if (Roll>315)
			key=3;
		else
			key=7;
	}
	else if (Pitch>225&&Pitch<315&&Roll>180&&Roll<240)
	{
		if (Roll<225)
			key=4;
		else
			key=8;
	}
	else
		key=0;		
}
/*************************************************************************************/
void change_min (void)//¸ù¾Ý·½Ïòµ÷ʱ¼ä
{

	if(key==1)
	{
		min+=1;
		min=num_test(min);
		delay_ms(50);
		return;
	}
	if(key==5)
	{
		min+=1;
		min=num_test(min);
		delay_ms(250);
		return;
	}
	if(key==2)
	{
		min-=1;
		min=num_test(min);
		delay_ms(50);
		return;
	}
	if(key==6)
	{
		min-=1;
		min=num_test(min);
		delay_ms(250);
		return;
	}
	if(key==3)
	{
		min+=10;
		min=num_test(min);
		delay_ms(50);
		return;
	}
	if(key==7)
	{
		min+=10;
		min=num_test(min);
		delay_ms(250);
		return;
	}
	if(key==4)
	{
		min-=10;
		min=num_test(min);
		delay_ms(50);
		return;
	}
	if(key==8)
	{
		min-=10;
		min=num_test(min);
		delay_ms(250);
		return;
	}	
}
/*************************************************************************************/
int num_test (int num)//¼ì²âÊý¾ÝÊÇ·ñ³¬³öÏÔʾ·¶Î§
{
	if (num<1)
	{
		num=99;
		return num;
	}
	if (num>99)
	{
		num=0;
		return num;
	}
}
/*************************************************************************************/
void timer_start (void)//´ò¿ª¶¨Ê±Æ÷0
{
	TMOD=0x01;
	TH0=(65536-50000)/256;
	TL0=(65536-50000)%256;
	EA=1;
	ET0=1;
	TR0=1;
}
/*************************************************************************************/
void timer0 () interrupt 1//¶¨Ê±Æ÷0ÖжÏ
{
	TH0=(65536-50000)/256;
	TL0=(65536-50000)%256;
	i++;
	if(i==1200)//20ԼΪһÃë ¹©²âÊÔʱʹÓà ʵ¼ÊʹÓÃʱÉèÖÃΪ1200 ԼΪһ·Ö
	{
		i=0;
		min--;
	}
}
/*************************************************************************************/
void delay_us(int a)//΢ÃëÑÓʱ ÓÃÓÚ1Tµ¥Æ¬»ú
{
	for (a;a>0;a--)
	     _nop_();
}
/*************************************************************************************/
void delay_ms(int a)//ºÁÃëÑÓʱ ÓÃÓÚ1Tµ¥Æ¬»ú
{
	unsigned char b,c;
	for (a;a>0;a--)
	{
		for(b=54;b>0;b--)
		{
			for(c=54;c>0;c--);
		}
	}
}
/*************************************************************************************/
void dis_data (unsigned int num)//ÏÔʾÊý¾Ý
{
	LED1=0;
	P1=disdata[num/10];
	delay_ms(10);
	LED1=1;
	LED2=0;
	P1=disdata[num%10];
	delay_ms(10);
	LED2=1;
	delay_ms(2);
}
/*************************************************************************************/
void beep (int x)//´ò¿ª·äÃùÆ÷  xΪÏìµÄ´ÎÊý
{
	int a; 
	for (x;x>0;x--)
	{
		a=0;
		while(a<50)//ÏìµÄʱ¼ä
		{
			BEEP=0;
			delay_ms(1);//Òôµ÷
			BEEP=1;
			delay_ms(1);
			a++;
		}
			delay_ms(150);
	}
}
/*************************************************************************************/
不准备参任何比赛了,把代码贴出来造福一下社会。因为IDE从μ4降到了μ3,中文注释都成了乱码,将就着看吧。

2019-04-02 15:00:06 qq_42156796 阅读数 2001
  • 定时器和计数-第1季第10部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第10个课程,主要内容是51单片机的定时器和计数器,本课程的学习目标是对定时器的作用和意义有深入理解,掌握通过操作寄存器来操作硬件的思路和方法。

    1963 人正在学习 去看看 朱有鹏

51单片机:利用定时器实现1秒后LED灯的状态改变

作业要求:

用定时器/计数器的定时功能控制实验板右下侧流水灯区域的最右侧的LED灯,初始上电时,该LED灯处于熄灭状态,每来1s,该LED灯改变一次状态。要求画出对应的电路原理图。

原理图:

在这里插入图片描述

代码如下:

#include<reg51.h>
sbit D1 = P1^0;

void main()
{
  TMOD &=0X0F;
  TMOD |=0x01;
  TH0 = (65536 - 50000)/256;//高四位初值
  TL0 = (65536 - 50000)%256;//低四位初值,每隔50ms溢出
  EA = 1; 	   //开总中断
  ET0 = 1;	   //T1开时定时器溢出
  TR0 = 1;	   //开启定时器
  while(1);
}

void ET0_ISR(void) interrupt 1	   //定时中断
{ 
  static unsigned int cnt = 0;	   
  TH0 = (65536-50000)/256;
  TL0 = (65536-50000)%256;
  cnt++;
  if(cnt >= 20)					 //当为1秒时D1灯状态改变
  {
    cnt = 0;
	D1 =~D1;
  }
}

仿真结果:

(因为录频软件原因,仿真结果不是很理想,但是可以前几秒还是可以看出来的)
在这里插入图片描述

2013-10-09 20:18:50 qq910876301 阅读数 2399
  • 定时器和计数-第1季第10部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第10个课程,主要内容是51单片机的定时器和计数器,本课程的学习目标是对定时器的作用和意义有深入理解,掌握通过操作寄存器来操作硬件的思路和方法。

    1963 人正在学习 去看看 朱有鹏

使用内部T2定时器制作时钟

 

目标:

使用89c52内部T2定时器制作时钟,每10ms中断一次,自动装载初值,向上计数溢出。

 

 

定时器2的资料:

 

 

T2的控制寄存器的功能描述如下:

T2CONT2的控制寄存器),字节地址0C8H

位地址   0CFH    0CEH   0CDH   0CCH   0CBH   0CAH   0C9H   0C8H

     TF2     EXF2   RCLK   TCLK   EXEN2  TR2    C/T2   CP/RT2

各位的定义如下:

TF2:定时/计数器2溢出标志,T2溢出时置位,并申请中断。只能用软件清除,但T2作为波特率发生器使用的时候,(RCLK=1TCLK=1),T2溢出时不对TF2置位。

EXF2:当EXEN2=1时,且T2EX引脚(P1.0)出现负跳变而造成T2的捕获或重装的时候,EXF2置位并申请中断。EXF2也是只能通过软件来清除的。

RCLK:串行接收时钟标志,只能通过软件的置位或清除;用来选择T1RCLK=0)还是T2RCLK=1)来作为串行接收的波特率产生器

TCLK:串行发送时钟标志,只能通过软件的置位或清除;用来选择T1TCLK=0)还是T2TCLK=1)来作为串行发送的波特率产生器

EXEN2T2的外部允许标志,只能通过软件的置位或清除EXEN2=0:禁止外部时钟触发T2EXEN2=1:当T2未用作串行波特率发生器时,允许外部时钟触发T2,当T2EX引脚输入一个负跳变的时候,将引起T2的捕获或重装,并置位EXF2,申请中断。

TR2T2的启动控制标志;TR2=0:停止T2TR2=1:启动T2

C/T2T2的定时方式或计数方式选择位。只能通过软件的置位或清除;C/T2=0:选择T2为定时器方式C/T2=1:选择T2为计数器方式,下降沿触发。

CP/RT2:捕获/重装载标志,只能通过软件的置位或清除CP/RT2=0时,选择重装载方式,这时若T2溢出(EXEN2=0时)或者T2EX引脚(P1.0)出现负跳变(EXEN2=1时),将会引起T2重装载;CP/RT2=1时,选择捕获方式,这时若T2EX引脚(P1.0)出现负跳变(EXEN2=1时),将会引起T2捕获操作。但是如果RCLK=1TCLK=1时,CP/RT2控制位不起作用的,被强制工作于定时器溢出自动重装载模式。

T2MOD(方式寄存器),字节地址0C9H

 

D7     D6       D5      D4       D3      D2      D1     D0

--      --      --      --       --      --      T2OE   DCEN

T2OET2输出允许位,当T2OE=1的时候,允许时钟输出到P1.0。(仅对80C54/80C58有效

DCEN向下计数允许位DCEN=1是允许T2向下计数,否则向上计数。

 

T2的数据寄存器TH2TL2T0T1的用法一样,而捕获寄存器RCAP2HRCAP2L只是在捕获方式下,产生捕获操作时自动保存TH2TL2的值。

    以上是T2的相关寄存器的描述,其实用法上跟T0T1是差不多的,只是功能增强了,设置的东西多了而已。

定时/计数器2其实用到最多的就是T2CON这个寄存器啦,它设定的定时和计数的方式。有三种工作方式,捕获,自动重装,波特率发生器。下面我是在百度百科里面找的少许资料:

捕获方式: 

  在捕获方式下,通过T2CON 控制位EXEN2 来选择两种方式。如果EXEN2=0,定时器2 是一个16 位定时器或计数器,计数溢出时,对T2CON 的溢出标志TF2 置位,同时激活中断。如果EXEN2=1,定时器2 完成相同的操作,而当T2EX 引脚外部输入信号发生1 至0 负跳变时,也出现TH2 和TL2 中的值分别被捕获到RCAP2H 和RCAP2L 中。另外,T2EX 引脚信号的跳变使得T2CON 中的EXF2 置位,与TF2 相仿,EXF2 也会激活中断。捕获方式如图4 所示。 

自动重装载(向上或向下计数器)方式: 

  当定时器2工作于16位自动重装载方式时,能对其编程为向上或向下计数方式,这个功能可通过特殊功能寄存器T2CON(见表5)的DCEN 位(允许向下计数)来选择的。复位时,DCEN 位置“0”,定时器2 默认设置为向上计数。当DCEN置位时,定时器2 既可向上计数也可向下计数,这取决于T2EX 引脚的值,参见图5,当DCEN=0 时,定时器2 自动设置为向上计数,在这种方式下,T2CON 中的EXEN2 控制位有两种选择,若EXEN2=0,定时器2 为向上计数至0FFFFH 溢出,置位TF2 激活中断,同时把16 位计数寄存器RCAP2H 和RCAP2L重装载,RCAP2H 和RCAP2L 的值可由软件预置。若EXEN2=1,定时器2 的16 位重装载由溢出或外部输入端T2EX 从1 至0 的下降沿触发。这个脉冲使EXF2 置位,如果中断允许,同样产生中断。 

定时器2 的中断入口地址是:002BH ——0032H 。 

  当DCEN=1 时,允许定时器2 向上或向下计数,如图6 所示。这种方式下,T2EX 引脚控制计数器方向T2EX 引脚为逻辑“1”时,定时器向上计数,当计数0FFFFH 向上溢出时,置位TF2,同时把16 位计数寄存器RCAP2H 和RCAP2L重装载到TH2 和TL2 中。 T2EX 引脚为逻辑“0”时,定时器2 向下计数,当TH2 和TL2 中的数值等于RCAP2H 和RCAP2L中的值时,计数溢出,置位TF2,同时将0FFFFH 数值重新装入定时寄存器中。 

当定时/计数器2 向上溢出或向下溢出时,置位EXF2 位。 

波特率发生器: 

  当T2CON(表3)中的TCLK 和RCLK 置位时,定时/计数器2 作为波特率发生器使用。如果定时/计数器2 作为发送器或接收器,其发送和接收的波特率可以是不同的,定时器1 用于其它功能,如图7 所示。若RCLK 和TCLK 置位,则定时器2工作于波特率发生器方式。 

  波特率发生器的方式与自动重装载方式相仿,在此方式下,TH2 翻转使定时器2 的寄存器用RCAP2H 和RCAP2L 中的16位数值重新装载,该数值由软件设置。 

  在方式1 和方式3 中,波特率由定时器2 的溢出速率根据下式确定: 

方式1和3的波特率=定时器的溢出率/16

 定时器既能工作于定时方式也能工作于计数方式,在大多数的应用中,是工作在定时方式(C/T2=0)。定时器2 作为波特率发生器时,与作为定时器的操作是不同的,通常作为定时器时,在每个机器周期(1/12 振荡频率)寄存器的值加1,而作为波特率发生器使用时,在每个状态时间(1/2 振荡频率)寄存器的值加1。波特率的计算公式如下: 

  方式1和3的波特率=振荡频率/{32*[65536-(RCP2H,RCP2L)]}式中(RCAP2H,RCAP2L)是RCAP2H 和RCAP2L中的16 位无符号数。 

  定时器2 作为波特率发生器使用的电路如图7 所示。T2CON 中的RCLK 或TCLK=1 时,波特率工作方式才有效。在波特率发生器工作方式中,TH2 翻转不能使TF2 置位,故而不产生中断。但若EXEN2 置位,且T2EX 端产生由1 至0 的负跳变,则会使EXF2 置位,此时并不能将(RCAP2H,RCAP2L)的内容重新装入TH2 和TL2 中。所以,当定时器2 作为波特率发生器使用时,T2EX 可作为附加的外部中断源来使用。需要注意的是,当定时器2 工作于波特率器时,作为定时器运行(TR2=1)时,并不能访问TH2 和TL2。因为此时每个状态时间定时器都会加1,对其读写将得到一个不确定的数值。 

  然而,对RCAP2 则可读而不可写,因为写入操作将是重新装载,写入操作可能令写和/或重装载出错。在访问定时器2或RCAP2 寄存器之前,应将定时器关闭(清除TR2)。 


 

T2定时器程序编写流程

 

T2定时器设置步骤:

 

第一步:设置T2MOD确定工作方式【向下计数法】

 

 

第二步:装初值【10ms一次中断

 

第三步:中断运行方式

 

 

第四步:开中断

 

 

 

第五步:中断服务程序:

 

 

 

 


项目实测:

中断服务程序中采用定时器2自动重装初值,虽然已经完全消除了“装值”产生的时间误差,但是在进入中断,出中断这个过程中还是产生了时间上的微小误差。由此带来的软件误差为每10ms1us的时间误差出现。这在程序中已经修正!到此,在软件上这个程序是绝对精准,但是由于硬件质量及环境等因素影响,依然产生了硬件误差。

 

 

下表是统计了24个小时之后得出的汇总:

说明:在20130826日晚21:33:45开始启动时钟测试

起始时间

此时北京时间

北京时间时间差

时钟上的时间

误差

21:33:45

7:53:48

10:20:03

10:20:00

-3

21:33:45

7:58:45

10:25:00

10:24:57

-3

21:33:45

9:31:15

11:57:30

11:57:26

-4

21:33:45

9:31:45

11:58:00

11:57:56

-4

21:33:45

9:33:49

12:00:04

12:00:00

-4

21:33:45

9:34:09

12:00:24

12:00:20

-4

21:33:45

15:09:25

17:35:40

17:35:34

-6

21:33:45

15:09:41

17:35:56

17:35:50

-6

21:33:45

21:30:25

23:56:25

23:56:32

-7

21:33:45

21:30:48

23:57:03

23:56:55

-8

21:33:45

21:31:08

23:57:23

23:57:15

-8

 

由此表记录的数据及观察白天可以得出一个结论:硬件误差是每三个小时时钟慢一秒钟。此处是人眼记录,可能偏差较大,但是不会超过300ms。到此此时钟硬件误差也得到修正,实际上还可以继续修正硬件误差,只是比较费时费力,需要统计一个月,再统计一年的误差,再修正。到目前为止,此时钟已是相当精准的,未硬件误差修正之前一年的误差为48分钟6秒,修正之后,可以确保一年的时间误差在5分钟之内。

 

2012-08-21 10:42:03 xmfthu 阅读数 4526
  • 定时器和计数-第1季第10部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第10个课程,主要内容是51单片机的定时器和计数器,本课程的学习目标是对定时器的作用和意义有深入理解,掌握通过操作寄存器来操作硬件的思路和方法。

    1963 人正在学习 去看看 朱有鹏

     用51单片机的定时器0的方式1计时,结果用4位数码管显示,分别为秒十位,秒个位,毫秒百位,毫秒十位。所以总共最大计时为1分钟,精确到了0.01s。同时,加上3个独立按键,分别控制秒表的清零,暂停和开始功能。

   按照模块化的思想,首先先把要用的函数和预定义写到一个头文件中,要用到的预定义和全局变量如下:

#include <reg52.h>
#define uchar   unsigned char

sbit Clear=P3^7;   //按键实现清零
sbit Stop=P3^6;    //按键实现暂停
sbit ReStart=P3^5;  //按键实现开始
sbit dula=P2^6;     //段选线接P2^6口
sbit wela=P2^7;     //位选线接P2^7口

//table数组中存储数码管显示0~F对应的编码
uchar code   table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};

//计时时间,计时中断发生一次,则加1,最大为5999
int time;

 

接下来是将要用到的函数声明:

//用于动态扫描数码管的时间较短的延时函数
void ShortDelay();    

//用于键盘消抖的时间较长的延时函数
void LongDelay();

//初始化
void Inital();

//在数码管显示秒十位,秒个位,微秒百位,微秒十位
//用拼音表示很不好,将就着看吧
void Display(uchar shimiao,uchar gemiao,uchar baiwei,uchar shiwei);

//清零函数,按下清零键的时候调用
void Clc();

//暂停函数,按下暂停键时候调用
void StopTimer();

//开始或重新开始函数,按下开始键时候调用
void ReStartTimer();

//键盘扫描函数,是否有键被按下,并且执行相应操作
void KeyScan();


 

接下来是主函数:

void main()
{
  uchar shimiao,gemiao,baiwei,shiwei;
  Inital();   //初始化
  while(1)
  {
    if(time==6000)    //到了秒表最大值,自动归零
    {
      time=0;
    }
    
    //分解出要显示的各个数位
    shimiao=time/1000;
    gemiao=time%1000/100;
    baiwei=time%100/10;
    shiwei=time%10;
    
    //显示
    Display(shimiao,gemiao,baiwei,shiwei);
    ShortDelay();
    //键盘扫描,有键按下执行相应操作
    KeyScan();
  }
}


下面的工作就是要写相应的处理函数了:

首先是两个延时函数:

void ShortDelay()
{
  uchar a=100;
  while(a--);
}

void LongDelay()
{
  int a=5000;
  while(a--);
}


初始化函数:

void Inital()
{
  dula=0;
  wela=0;
  time=0;
  EA=1;
  ET0=1;    //开定时器0中断
  TMOD=0x01;
  TH0=0xdc;
  TL0=0x01;   //装载初值,计时方式1  
  Clear=1;
  Stop=1;
  ReStart=1;   //按键接口管脚输出高电平
}


在数码管上显示时间的函数:

void Display(uchar shimiao,uchar gemiao,uchar baiwei,uchar shiwei)
{
  //输出秒十位
  wela=1;
  P0=0xfb;
  wela=0;
  P0=0xff;
  dula=1;
  P0=table[shimiao];
  dula=0;
  ShortDelay();
  
  //输出秒个位
  wela=1;
  P0=0xf7;
  wela=0;
  P0=0xff;
  dula=1;
  P0=table[gemiao];
  dula=0;
  ShortDelay();
  
  wela=1;
  P0=0xef;
  wela=0;
  P0=0xff;
  dula=1;
  P0=table[baiwei];
  dula=0;
  ShortDelay();
  
  wela=1;
  P0=0xdf;
  wela=0;
  P0=0xff;
  dula=1;
  P0=table[shiwei];
  dula=0;
  ShortDelay();
  
}


键盘扫描函数:

void KeyScan()
{
   if(Clear==0)    
    {
      LongDelay();    //消抖
      if(Clear==0)    //确实是被按下
      {
        //Do somthing
        Clc();
      }
    }
    
    //Stop和ReStart检测方法相同
    if(Stop==0)     
    {
      LongDelay();
      if(Stop==0)
      {
        //Do somthing
        StopTimer();
      }
    }
    if(ReStart==0)
    {
      LongDelay();
      if(ReStart==0)
      {
        //Do somthing
        ReStartTimer();
      }
    }
}


最后是3个按键按下后的处理函数:

void Clc()
{
  TR0=0;   //计时暂时停止
  time=0;   //时间归零
  Display(0,0,0,0);   //显示0000
  ShortDelay();    
}

void StopTimer()
{
  //计时暂时停止,仅此而已
  TR0=0;    
}

void ReStartTimer()
{
  //计时重新开始,仅此而已
  TR0=1;
}


至此,编译链接后生成的.HEX文件就能下载到51单片机里了。

 

51单片机测频

阅读数 2735

没有更多推荐了,返回首页