ds1302

标 签:

    stc12系列,stc15系列的ds1302的驱动程序,ds1302我就不在这里多介绍,当你用这个芯片的时候你应该会了解了他的一些基本的参数,功能特点了吧,我就不多说了,直接上程序。

    首先新建项目,这个我也不多说了,我博客里面有,不会的自己找找。

    第一步,新建 ds1302.h 文件,代码如下:

#ifndef __DS1302_H
#define __DS1302_H
#include "INTRINS.H"
#include "STC15Wxx.H"
//#include "STC12C5A.H"

sbit DS1302_IO = P3^3;
sbit DS1302_CE = P5^5;
sbit DS1302_CL = P3^2;

//定义变量
extern unsigned char time[7] = {0x00,0x16,0x18,0x19,0x04,0x06,0x17};

//声明函数
void DS1302BurstWrite();//利用ds1302的突发模式一次读取八个字节
void DS1302BurstRead(unsigned char leg);//利用ds1302的突发模式一次写入八个字节
unsigned char DS1302SingleRead(unsigned char addr);//读取ds1302上的某个寄存器地址值
void DS1302SingleWrite(unsigned char addr,unsigned char val);//给ds1302上的某个寄存器地址写入值

#endif

    第二步,新建 ds1302.c 文件,代码如下:

#include "ds1302.h"

//iic通信开始信号
void IIC_Start(){
	DS1302_CE = 0;
    DS1302_CL = 0;
    DS1302_IO = 0;
    _nop_();
    _nop_();
    _nop_();
    DS1302_CE = 1;
}

//iic通信结束信号
void IIC_End(){
	DS1302_CE = 0;
    _nop_();
    DS1302_CL = 1;
    _nop_();
    DS1302_IO = 1;
}

//在iic信道上写入ds1302的寄存器地址
void DS1302ByteWrite(unsigned char dat){
    unsigned char i;

    for(i=0;i<8;i++){ //循环写入八位地址位
        dat >>= 1;
        DS1302_IO = CY;
        DS1302_CL = 1;
        DS1302_CL = 0;
    }
}

//在iic信道上读取ds1302的寄存器值
unsigned char DS1302ByteRead(){
    unsigned char i,dat = 0x00;   

    for(i=0;i<8;i++){   //循环读取八位数据位
        if(DS1302_IO){
            dat |= 1 << i;
        }
        DS1302_CL = 1;
        DS1302_CL = 0;
    }
    return dat;
}

//利用ds1302的突发模式一次读取八个字节
void DS1302BurstRead(unsigned char leg){
    unsigned char i;     

    IIC_Start(); //在iic信道上写入开始信号
    DS1302ByteWrite(0xBF);//写入ds1302突发读命令
    for(i=0;i < leg;i++){ //循环读取leg个字节,小于八个字节
        time[i] = DS1302ByteRead(); //读取ds1302的值
    }
    DS1302End(); //在iic信道上写入结束信号
}

//利用ds1302的突发模式一次写入八个字节
void DS1302BurstWrite(){
    unsigned char i;
       
    DS1302Start();//在iic信道上写入开始信号
    DS1302ByteWrite(0xBE);//写入ds1302突发写命令
    for(i=0;i < 8;i++){//循环写入八个字节
        DS1302ByteWrite(time[i]); //给ds1302的寄存器写入值
    }
    DS1302End();//在iic信道上写入结束信号
}

//读取ds1302上的某个寄存器地址值
unsigned char DS1302SingleRead(unsigned char addr){
    unsigned char dat;
    
    DS1302Start();//在iic信道上写入开始信号
    DS1302ByteWrite(addr);
    dat = DS1302ByteRead();//读取该寄存器地址的值
    DS1302End();//在iic信道上写入结束信号
    
    return dat;
}

//给ds1302上的某个寄存器地址写入值
void DS1302SingleWrite(unsigned char addr,unsigned char val){  
    DS1302Start();//在iic信道上写入开始信号
    DS1302ByteWrite(addr);//写入ds1302寄存器地址
    DS1302ByteWrite(val);//给该寄存器地址写入值
    DS1302End();//在iic信道上写入结束信号
}

    第三步,新建 main.c 文件,代码如下:

#include "STC15Wxx.h"
#include "ds1302.h"

//定义变量
unsigned int sec;


//声明函数
void UartInit(void);//串口初始化
void Timer0Init(void);//定时器0初始化
void sendData(unsigned char dat);//发送串口数据

void main(){
    EA = 1;//开启总中断
    
    //初始化功能模块
    UartInit();
    Timer0Init();

    while(1){
        if(sec > 200){//如果做时钟,最好200左右
            sec = 0;
            DS1302BurstRead(3);//看你显示大小,如果只显示秒、分、时,只读三位就好
            sendData(time[0]);//发送秒到串口
        }
    }
}

//串口初始化
void UartInit(void)		//4800bps@12.000MHz
{
	SCON = 0x50;		//8位数据,可变波特率
	AUXR |= 0x01;		//串口1选择定时器2为波特率发生器
	AUXR |= 0x04;		//定时器2时钟为Fosc,即1T
	T2L = 0x8F;		//设定定时初值
	T2H = 0xFD;		//设定定时初值
	AUXR |= 0x10;		//启动定时器2
}

//发送一个字节到串口函数
void sendData(unsigned char dat){
    SBUF = dat;//写入发送值到串口缓存中
    while(!TI);//等待数据发送完闭
    TI = 0;//清除发送寄存器状态
}

//定时器0初始化
void Timer0Init(void)		//1毫秒@12.000MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x20;		//设置定时初值
	TH0 = 0xD1;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
    ET0 = 1; //开启定时器中断
}

//定时器0中断
void T0() interrupt 1{
    sec++; //中断次数
}


51-STC / 评 论 (0) / 热度 (272℃) / 2017-04-20 / 阅读全文  / MaWei

多按键扫描程序

标 签:

    stc系列多按键扫描程序,一般的按键都是读按键状态然后延时10ms再读一次状态 ,如果两次一样就说明按下了。

这样如果单程序还可以,如果程序多了,就不行了,延时10ms对于多程序调用是致命的漏洞了,肯定是不行。我

下面所用的是另一种方法,这种方法效力高,也是简单。我就不多说,直接上程序了。

#include "INTRINS.H"
#include "STC12C5A.H"
//#include "STC15F2K.H"

typedef unsigned char u8;
typedef unsigned int u16;

sbit K1=P1^4;
sbit K2=P1^5;
sbit K3=P1^6;
sbit K4=P1^7;
sbit K5=P3^5;

//函数声明
void Timer0Init();//定时器0初始化
void DelayXms(u16 n);//软件延时
void keyDriver();//按键驱动

//全局变量声明
u8 keySta[] = {1,1,1,1,1};//按键状态
u8 keyIndex;//按键索引

void main(){
    EA = 1;
    //初始化定时器0
    Timer0Init();
    
    while(1){
        keyDriver();//按键驱动
        if(keyIndex == 1){
            P20 = ~P20;//LED点亮
            keyIndex = 0;
        }
        
        //key();
    }
}

//按键驱动
void keyDriver(){
    static keybak[] = {1,1,1,1,1};//按键上次状态备份
    u8 i;
    
    for(i=0;i<5;i++){//循环按键状态处理
        if(keybak[i] != keySta[i]){//如果这次状态和上次状态不一样,说明有按键动作
            if(keybak[i] == 1){//如果上次是弹起,又与上次状态不一样,说明按下了
                keyIndex = i+1;//输出按键索引
            }
        }
        keybak[i] = keySta[i];//把这次状态保存
    }
}

//按键扫描程序
void keyScan(){
    u8 i;
    static keybuf[] = {0xFF,0xFF,0xFF,0xFF,0xFF};//按键扫描缓存
    
    //将当前的状态移入缓存
    keybuf[0] = (keybuf[0] << 1) | K1;//按键上
    keybuf[1] = (keybuf[1] << 1) | K2;//按键右
    keybuf[2] = (keybuf[2] << 1) | K3;//按键下
    keybuf[3] = (keybuf[3] << 1) | K4;//按键左
    keybuf[4] = (keybuf[4] << 1) | K5;//按键中
    
    for(i=0;i<5;i++){//循环按键读取状态,消抖处理
    	if(((keybuf[i] & 0x0F) == 0x00)){//循环读取按键状态,如果4次都读取的是低电低,就说明按下了
    		keySta[i] = 0;//缓存状态
		}else if(keybuf[i] & 0x0F == 0x0F){//循环读取按键状态,如果4次都读取的是低电低,就说弹起了
			keySta[i] = 1;//缓存状态
		}
    }
}

//定时器0初始化
void Timer0Init(void)		//1毫秒@12.000MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x20;		//设置定时初值
	TH0 = 0xD1;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
    ET0 = 1;    //开启定时器0中断
}

//定时器按键扫描
void T0() interrupt 1{
    keyScan();//扫描按键
}

//软件1ms延时
void Delay1ms()		//@12.000MHz
{
	unsigned char i, j;

	_nop_();
	_nop_();
	i = 12;
	j = 168;
	do
	{
		while (--j);
	} while (--i);
}

//延时n秒
void DelayXms(u16 n){
    while(n--){
        Delay1ms();
    }
}


51-STC / 评 论 (0) / 热度 (430℃) / 2017-04-04 / 阅读全文  / MaWei

51 超声波测距

标 签:

本教程用的硬件清单:

    单片机:stc12c5a60s2、stc15w408as、stc15f2k60等

    超声波模块:HC-SR04

超声波测距的特点就是便宜,缺点就不说了,主要说说超声波器距离模块的用法原理吧,模块图如下:

2017032910503010506.png2017032910511011227.png

2017032910541752019.png

好了,超声波模块的简介就这么多了,话不多说,直接上程序


#include "STC12C5A.H"

typedef unsigned char u8;
typedef unsigned int u16;

sbit Trig = P2^1;
sbit Echo = P2^0;

//声明函数
void getDistance();//获取距离
void Timer0Init();//初始化定时器0
void UartInit();//初始化串口
void SendData(u8 data_buf); //发送数据到串口
void DelayXms(u16 n); //延时N ms

//定义全局变量
u8 cnt=0; //定时器0中断次数
u16 time; //计时
u16 distance; //距离


void main(){
    EA = 1;//开启总中断

    UartInit();//初始化串口
    Timer0Init();//初始化定时器0

    while(1){
        getDistance(); //获取距离
        DelayXms(800); //延时
    }

}

//获取距离
void getDistance()
{
    u8 i,j=60;
    u8 str[] = "D:-.--M";

    TH0 = TL0 = cnt = 0; //初始化定时器0
    Trig = 1; //拉高超声波模块触发IO
    while(j--); // 延时20us
    Trig = 0; //拉低超声波模块触发IO
    while(!Echo);//等待超声波模块输出IO拉高 
    TR0 = 1; //开启定时器计时
    while(Echo); //等待超声波模块输出IO拉低
     TR0 = 0; //关闭定时器计时

     //判断是否超出模块最大测距 4m
     if(cnt < 5){
         //计算出时间
        time = (TH0 * 256 + TL0 + cnt * 65536) * (1 / 12.000) + 0.5;
        //计算出距离 340m/s 或 us/58cm
        distance = time / 58 + 0.5;
    }else
        distance = 0;

    str[2] = (distance % 1000 / 100) + '0';
    str[4] = (distance % 100 / 10) + '0';
    str[5] = (distance % 10) + '0';
    //从串口发出,也可以用显示器显示,自己接显示驱动就可以了
    for(i=0;i<7;i++){
        SendData(str[i]);
    }
    SendData('\n');
    //复位定时器
    cnt = 0;
    TH0 = TL0 = 0;
}
//定时器0寄存器初始化
void Timer0Init(void)        //100微秒@12.000MHz
{
    AUXR |= 0x80;        //定时器时钟1T模式
    TMOD &= 0xF0;        //设置定时器模式
    TMOD |= 0x01;        //设置定时器模式
    TL0 = 0x50;        //设置定时初值
    TH0 = 0xFB;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 0;        //定时器0开始计时
    ET0 = 1; //开启定时器中断
}

void T0() interrupt 1
{
    cnt++;
}
//串口初始化定时器1
void UartInit(void)        //4800bps@12.000MHz
{
    SCON=0x52;                  
    AUXR &= 0xFE;
    TMOD=0x20;    
    TH1=TL1=0xf3;
    PCON=0x80;     
    TR1=1;
}

//发送一个字符
void SendData(u8 data_buf)
{
    SBUF = data_buf;
    while(!TI);//TI是发送成功标志
    TI = 0;
}

//延时1毫秒
void Delay1ms()        //@12.000MHz
{
    unsigned char i, j;

    i = 12;
    j = 169;
    do
    {
        while (--j);
    } while (--i);
}
//延时n毫秒
void DelayXms(u16 n)
{
    while(n--){
        Delay1ms();
    }
}


51-STC / 评 论 (0) / 热度 (237℃) / 2017-03-29 / 阅读全文  / MaWei

51 红外解码

标 签:

    本教程是基于NEC红外协议的解码程序。其它的协议自己去找资料,我也不知道怎么玩。

    先来说说NEC协议吧,NEC 协议的数据格式包括了引导码、用户码、用户码(或者用户码反码)、按键键码和键码反码,最后一个停止位。停止位主要起隔离作用,一般不进行判断,编程时我们也不予理会。其中数据编码总共是 4 个字节 32 位。第一个字节是用户码,第二个字节可能也是用户码,或者是用户码的反码,具体由生产商决定,第三个字节就是当前按键的键数据码,而第四个字节是键数据码的反码,可用于对数据的纠错。下图就是NEC发射的编码

2017032118041762684.png

引导码:9ms 的载波+4.5ms 的空闲。
比特值“0”:560us 的载波+560us 的空闲。
比特值“1”:560us 的载波+1.68ms 的空闲。

    现在知道了NEC协议了,代码就好写了,但也只是相对低级的单片机,比如(stc89系列的)用传统的解码方式方法,就是用单片机计时器一位一位的计算高低电平的时间。但由于现在的单片机越来越快,因些计时器最高计时越来越短了,用传统的解码方法就不行了。至于传统方法的解码程序自己上网去找,多的是。

    下面说的一种方法是阿莫论坛上看到有人发了一种特别的解码方式,他不管协议头的高低电平时间长短,只关注高低电平脉冲的时间周期,周期超过3ms的脉冲,他就认为是错误数据放弃,周期小于3ms的,他就尝试解码。周期为1.12ms的组合表示二进制的“0“, 周期为2.25ms的组合表示二进制的“1”,他发现周期小于3ms的脉冲,进而判断周期是否大于1.875ms,大于1.875ms就是数据1,否则是数据0。这种解码方式非常高效,而且代码很简单。唯一的缺点就是占用CPU率很高。定时器0要125us中断一次,非常占系统资源。

    硬件列表就不上了,也没有什么硬件,HS0038B一体化接收头,stc12系列及以上的都可以。算了也是上个HS0038B外部接线图吧:

2017032215225336729.png

   下面直接上代码,自己改里面的IO定义就好:

#include <reg51.h>
#include <intrins.h>

#define u8 unsigned char
#define u16  unsigned int

//* STC15W404AS寄存器补充 
sfr AUXR1 = 0XA2;
sfr AUXR = 0X8E;
sfr TH2 = 0XD6;
sfr TL2 = 0XD7;
sfr P4 = 0xc0;
sfr P5 = 0xc8;
sfr P3M0=0xB2;
sfr SPSTAT      =   0xCD;   //
sfr SPCTL       =   0xCE;   //
sfr SPDAT       =   0xCF;   //

//定时器2
sfr T2H=0xd6;
sfr T2L=0xd7;
sfr IE2=0xaf;


sbit IR_IO = P3^2;          // IR管脚 任意IO
bit Irprot_LastState = 0;   // 端口状态位
u8 codeCnt = 0;          // 数据码位计数
u8 irTime;                   // 码时间,用于以125us时间计时
u8 IR_data[4]={48,49,50,51}; // 接收数据缓存

void Delay1ms()        //@12.000MHz
{
    unsigned char i, j;

    i = 12;
    j = 169;
    do
    {
        while (--j);
    } while (--i);
}


void delay_ms(unsigned int i)
{
   while(i--)Delay1ms();
}


void Timer0Init(void)        //125微秒@12.000MHz
{
    AUXR |= 0x80;        //定时器时钟1T模式
    TMOD &= 0xF0;        //设置定时器模式
    TL0 = 0x24;        //设置定时初值
    TH0 = 0xFA;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0=1;            //打开定时器0中断
    EA=1;            //打开全部中断
}


void UartInit(void)        //4800bps@12.000MHz
{
    SCON = 0x50;        //8位数据,可变波特率
    AUXR |= 0x01;        //串口1选择定时器2为波特率发生器
    AUXR |= 0x04;        //定时器2时钟为Fosc,即1T
    T2L = 0x8F;        //设定定时初值
    T2H = 0xFD;        //设定定时初值
    AUXR |= 0x10;        //启动定时器2
}

void SendData(u8 data_buf) //发送一个字符
{
    SBUF = data_buf;
    while(!TI);//TI是发送成功标志
    TI = 0;
}



void main(void)
{
    u8 i;
    delay_ms(200); //上电延时200ms等待系统稳定
    UartInit();     //串口1初始化
    Timer0Init();//定时器0初始化
    while(1)
    {
        if(codeCnt==31)    //数据接收完成
        {
            if(IR_data[2]==~IR_data[3])//校验一下反码
            {
                for(i=0;i<4;i++) SendData(IR_data[i]);//输出4位hex
                delay_ms(30);//延时30ms等待数据超时,遥控器一次发3组相同的数据,这里用延时只接收1组就好了。
            }
        }
    }
}

//定时器0中断函数 125us
void Timer0() interrupt 1
{
   irTime++; //计时增加125us
   if(irTime==240) {irTime--;  codeCnt=0x3f;} // ir解码后码值存放时间, 240*125us = 30ms  0x3f=64
   if(IR_IO)   Irprot_LastState=1; // 记录IO状态
   else if(Irprot_LastState)       // 有下降沿,并且上个状态是高电平,表示红外管收到数据
   {
      Irprot_LastState = 0;        // 下降沿后IO状态记录为0
      if(irTime<24) // 小于24*125us=3ms的间隔才进行处理    因为红外线的0的周期1.125ms,1的周期2.25ms
      {
         codeCnt++; //数据码位计数+1
         codeCnt &= 0x1f; //等效if(codeCnt>0x1f) codeCnt=0x00; 这种操作比if判断更节约时间 0x1f=31
         IR_data[codeCnt>>3] <<= 1; //codeCnt>>3的范围(0~3),等效于IR_data[i]向左移动1位 (i范围0~3)
         if( irTime>15 )  IR_data[codeCnt>>3]++;  //大于15*125us=1.875ms的间隔为数据1 ,1就+1,0就不变。
      }
      irTime = 0;                  // 下降沿处理完成,将时间清0
   }    
}

以上代码为@浅雪大神友情提供

51-STC / 评 论 (0) / 热度 (616℃) / 2017-04-04 / 阅读全文  / MaWei

如何把STC驱动和头文件加入Keil

标 签:

    我想学习单片机,大部份会选择51单片机,其中stc的单片机为主要学习的对象。

    学习stc单片机,第一步肯定是先要有个编译器,keil编辑器是首选吧,至少我只知道keil编辑器最方便,但他的代码编辑不是太好。没有代码提示,也没有什么操作的快捷键。其实还有一个编辑器codeblocks,这个编辑器代码编辑起来很舒服,但编译51配置有点复杂,我也没有搞好,就不说了。

    好了,不说费话了,下面就说说怎么把stc的驱动库加入到keil软件里面,

    首先肯定是得安装好keil,然后再去stc的官网去下载stc-isp,Stc程序下载软件。下载后打开,找到 keil仿真设置菜单 ,点击选项栏,就看到了添加型号和头文件到keil中,点击选择keil的安装目录,如下图:2017031818013796367.png

点击OK后就把驱动添加到了keil软件里面了。然后在keil里面新建项目的时候就可以选择stc的型号了。


下面说说把头文件加入到keil软件里面,还是上面的菜单栏,找到头文件菜单栏,(没有看到,把软件放大)。如下图:

选择你要添加的单片机型号,然后点击保存文件,然后选择keil的安装目录下的 C51 -> INC ,然后写文件名,可以照它软件给的文件名保存,

比如STC15系列的保存名 STC15Wxx.H

以后在项目中要载入STC的头文件就可以写成:

#include "STC15Wxx.H

51-STC / 评 论 (0) / 热度 (279℃) / 2017-04-04 / 阅读全文  / MaWei

4位数码管显示

标 签:

首先说说要准备的硬件:

1.四位LED数码管,我选择是共阴管,3641AS四位共阴管。共阳也可以,其实共阴和共阳就是公共端就正接负的区别

2.STC15W408AS-SOP16 或 STC15W204S-SOP16都可以,注意是16脚的

3.串品程序下载器


四位LED数码管显示,3641AS显示其实明白了很简单,就是四组八位二极管控制。先看看四位共阴引脚图。

2017031421234728295.jpg

四位LED显示实际不是真的四位都在同一时间显示,其实是循环交替显示,只是变换的很快。

之所以看到都显示是因为画面暂留现象,眼睛一秒钟只是分辨24帧以下的动画。超过24帧后人眼就会看成动画。或者分辨不出来就闪动的效果。但为了效果好一点,我这里选择每秒100帧,也就是100Hz,所以四位加起来交替在10ms以内就可以了。


单片机用转接板SOP16焊好,然后4 位LED数码管插到上面的两排洞上,把LED的数码管的脚朝外掰一点就可以插进去了。

注意是单片上的点和LED数码管的点都在一边,也就是单片机 P1^2 脚接LED数码管的 4 脚 ,然后就是数码管下边的最后一个脚就跳过一个洞,我图上标出来了。

下面直接上程序了:

#include "STC15Wxx.h"

#define u8 unsigned char
#define u16 unsigned int

//共阴极数码管的0 1 2 3 4 5 6 7 8 9 A b C d E F
u8 code Dig[17]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
                 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71,
                 0x00};
//---A--
//|F   |B
//|       |
//---G---
//|E   |C
//|    |
//---D-- .H
//
//4位共阴数码管脚图

//   L1----A----F----L2----L3----B
//                 
//   ---LED1--LED2--LED3--LED4---
//                 
//   E-----D----H----C-----G----L4
                 
//数码管定义
sbit L1 = P3^2; //数码管第一位共阴脚
sbit L2 = P3^7; //数码管第二位共阴脚
sbit L3 = P1^0; //数码管第三位共阴脚
sbit L4 = P1^2; //数码管第四位共阴脚
/*------------*/
sbit LA = P3^3; //数码管A段
sbit LB = P1^1; //数码管B段
sbit LC = P1^4; //数码管C段
sbit LD = P5^4; //数码管D段
sbit LE = P5^5; //数码管E段
sbit LF = P3^6; //数码管F段
sbit LG = P1^3; //数码管G段
sbit LH = P1^5; //数码管H段,小数点
                 
//变量定义
u16 cnt = 0,second = 0,num; //时间
u8 LedNum = 3; //当前显示数码管序号
u8 TempVal[5] = {1,2,3,4};
u8 dp = 0;

//声明函数
void Timer0Init(void);//定时器0寄存器初始化
void showNum(u8 dat,u8 num);//显示数据

void main(){
    u16 temp = 0;
    EA = 1;
    
    //下面寄存器是开启强推挽输出,就是输出电流大,
    //开启了强推挽输出后LED是高亮
    P1M0 = 0x3F;
    P3M0 = 0xCE;
    P5M0 = 0x10;
    
    //初始化定时器0
    Timer0Init();
    //初始化ds18b20
    
    
    while(1){
        if(second > 1000){
            second = 0;
            num ++;
            
            TempVal[0] = (num % 10000 / 1000);
            TempVal[1] = (num % 1000 / 100);
            TempVal[2] = (num % 100 / 10);
            TempVal[3] = (num % 10);
        }
    }
}

//显示数据
void showNum(u8 dat,u8 num){
    L1 = L2 = L3 = L4 = 0; //消抖

    //数据段位赋值
    //此方式为位移缓存操作文件
    dat >>= 1;
    LA = CY;
    dat >>= 1;
    LB = CY;
    dat >>= 1;
    LC = CY;
    dat >>= 1;
    LD = CY;
    dat >>= 1;
    LE = CY;
    dat >>= 1;
    LF = CY;
    dat >>= 1;
    LG = CY;
    dat >>= 1;
    LH = CY;
    //此方式为位操作,这两促都可以,上面这种方式更优化,更快
//    LH=dat&0x80;
//    dat=(dat<<1);
//    LG=dat&0x80;
//    dat=(dat<<1);
//    LF=dat&0x80;
//    dat=(dat<<1);
//    LE=dat&0x80;
//    dat=(dat<<1);
//    LD=dat&0x80;
//    dat=(dat<<1);
//    LC=dat&0x80;
//    dat=(dat<<1);
//    LB=dat&0x80;
//    dat=(dat<<1);
//    LA=dat&0x80;
    
    //显示数码管第几位
    switch(num){
        case 0:
            L1 = 0;
            break;
        case 1:
            L2 = 0;
            break;
        case 2:
            L3 = 0;
            break;
        case 3:
            L4 = 0;
            break;
        default:
            L1 = L2 = L3 = L4 = 1;
    }
}

//定时器0寄存器初始化
void Timer0Init(void)        //1毫秒@12.000MHz
{
    AUXR |= 0x80;        //定时器时钟1T模式
    TMOD &= 0xF0;        //设置定时器模式
    TL0 = 0x20;        //设置定时初值
    TH0 = 0xD1;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0 = 1;
}

//定时器0中断函数
void T0() interrupt 1{
    second ++;
    cnt ++;
    
    if(cnt > 3){ //显示时间,如果这个设置大了,就出现闪烁,太小了也不
        cnt = 0;
        showNum(Dig[TempVal[LedNum]],LedNum);//显示字符,先选择显示的位,然后转换成LED显示数码位
        LedNum ++ ; //显示那位数码管
        LedNum &= 0x03;//位操作,相当于0-3;
    }
}


51-STC / 评 论 (0) / 热度 (244℃) / 2017-05-11 / 阅读全文  / MaWei

数码管DS18B20程序

标 签:

    这教程是用stc15、4位LED数码管、DS18B20制作温度计。

    制作硬件清单:

    单片机:STC15W204S-SOP16 或者 STCW408AS-SOP16

    显示:四位LED数码管,3641AS \ 3641BH等

    温度模块:DS18B20

    些教程是基于《4位数码管显示》教程成果上制作,具体可以在本站搜索 4位数码管显示 教程,学好了那个教程,并制作也来,那这个制作出来就很容易了。

    其实就是在4位数码管显示加 DS18B20 模块,因为ds18B20是单总线。所以只需要一个IO口。这制作DS18B20的数据IO接 P31

    DS18B20硬件相关参数,我就不在这里写了,自己在网上搜索手册看看。

    下面就不多说了,上程序吧,我是用文件方式写的,把 ds18b20 硬件驱动分开写。用Keil新建项目,

    新建文件:init.h

#ifndef __INIT_H
#define __INIT_H

#include "STC15F2K.H"
#include "DS18B20.H"
#include "INTRINS.H"

#define uchar unsigned char
#define uint  unsigned int
#define u8 unsigned char
#define u16  unsigned int
#define u32  unsigned long int


#endif

    新建文件:DS18B20.H

#ifndef __DS18B20_H
#define __DS18B20_H
#include "init.h"

/*****set pin********/
sbit DS18B20_DQ = P3^1;

u16 getTemp();

#endif

    新建文件:DS18B20.C

#include "DS18B20.H"

void delay1xus(u16 t){
    while(t--){
        _nop_();
        _nop_();
        _nop_();
        _nop_();
    }
}

/***** initialization ds18b20ry *********/
bit DS18B20Init(){
    bit stat = 1;
    
    DS18B20_DQ = 0;
    delay1xus(600);
    DS18B20_DQ = 1;
      delay1xus(60);
    stat = DS18B20_DQ;
    while(!DS18B20_DQ);
    
    return stat;
}

/******* ds18b20 write cmmand *******/
void DS18B20WritCmd(u8 dat){
    u8 i=0;
    
    for(;i < 8;i++){
        DS18B20_DQ = 0;
        delay1xus(2);
        if((dat>>i)&0x01)
            DS18B20_DQ = 1;
        delay1xus(60);
        DS18B20_DQ = 1;
        delay1xus(10);
    }

}

u8 DS18B20Read(){
    u8 i=0,dat = 0x00;
    
    for(;i < 8;i++){
        DS18B20_DQ = 0;
        delay1xus(2);
        DS18B20_DQ = 1;
        delay1xus(2);
        if(DS18B20_DQ)
            dat |= 1 << i;
        delay1xus(60);
    }
    
    return dat;
}

/***** start ds18b20 temp convert *************/
void DS1B20CT(){
    DS18B20Init();
    delay1xus(5);
    DS18B20WritCmd(0xCC);
    DS18B20WritCmd(0x1F);
    DS18B20Init();
    delay1xus(5);
    DS18B20WritCmd(0xCC);
    DS18B20WritCmd(0x44);
}

/***** start ds18b20 temp Read *************/
void DS1B20ReadCmd(){
    DS18B20Init();
    delay1xus(5);
    DS18B20WritCmd(0xCC);
    DS18B20WritCmd(0xBE);
}

/******** read temp *************/
u16 getTemp(){
    u8 LSB,MSB;
    u16 temp;
    
    DS1B20CT();
    DS1B20ReadCmd();
    LSB = DS18B20Read();
    MSB = DS18B20Read();
    temp = ((int)MSB << 8) + LSB;
        
    return temp;
}

    新建文件:main.c

#include "init.h"

//共阴极数码管的0 1 2 3 4 5 6 7 8 9 A b C d E F
u8 code Dig[17]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
                 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71,
                 0x00};
//---A--
//|F   |B
//|       |
//---G---
//|E   |C
//|    |
//---D-- .H
//
//4位共阴数码管脚图

//   L1----A----F----L2----L3----B
//                 
//   ---LED1--LED2--LED3--LED4---
//                 
//   E-----D----H----C-----G----L4
                 
//数码管定义
sbit L1 = P3^2; //数码管第一位共阴脚
sbit L2 = P3^7; //数码管第二位共阴脚
sbit L3 = P1^0; //数码管第三位共阴脚
sbit L4 = P1^2; //数码管第四位共阴脚
/*------------*/
sbit LA = P3^3; //数码管A段
sbit LB = P1^1; //数码管B段
sbit LC = P1^4; //数码管C段
sbit LD = P5^4; //数码管D段
sbit LE = P5^5; //数码管E段
sbit LF = P3^6; //数码管F段
sbit LG = P1^3; //数码管G段
sbit LH = P1^5; //数码管H段,小数点
                 
//变量定义
u16 cnt = 0,second = 0; //时间
u8 LedNum = 3; //当前显示数码管序号
u8 TempVal[5] = {1,2,3,4};
u8 dp = 0;


//声明函数
void Timer0Init();
void UartInit();
void SendData(u8 data_buf);
void showNum(u8 dat,u8 num);


void main(){
    u16 temp = 0;
    EA = 1;
    
//    P1M0 = 0x20;
//    P3M0 = 0x80;
//    P1M0 = 0x3F;
//    P3M0 = 0xCE;
//    P5M0 = 0x10;
    
    //串口初始化
    UartInit();
    //初始化定时器0
    Timer0Init();
    //初始化ds18b20
    
    
    while(1){
        if(second > 1000){
            second = 0;
            
            temp = getTemp();
            temp = temp * 0.0625 * 100 + 0.5;
            TempVal[0] = (temp % 10000 / 1000);
            TempVal[1] = (temp % 1000 / 100);
            TempVal[2] = (temp % 100 / 10);
            TempVal[3] = (temp % 10);
        }
    }
}

//显示数据
void showNum(u8 dat,u8 num){
    L1 = L2 = L3 = L4 = 1; //消抖

    //数据段位赋值
    dat >>= 1;
    LA = CY;
    dat >>= 1;
    LB = CY;
    dat >>= 1;
    LC = CY;
    dat >>= 1;
    LD = CY;
    dat >>= 1;
    LE = CY;
    dat >>= 1;
    LF = CY;
    dat >>= 1;
    LG = CY;
    dat >>= 1;
    LH = CY;
//    LH=dat&0x80;
//    dat=(dat<<1);
//    LG=dat&0x80;
//    dat=(dat<<1);
//    LF=dat&0x80;
//    dat=(dat<<1);
//    LE=dat&0x80;
//    dat=(dat<<1);
//    LD=dat&0x80;
//    dat=(dat<<1);
//    LC=dat&0x80;
//    dat=(dat<<1);
//    LB=dat&0x80;
//    dat=(dat<<1);
//    LA=dat&0x80;
    
    //显示数码管第几位
    switch(num){
        case 0:
            L1 = 0;
            break;
        case 1:
            L2 = 0;
            break;
        case 2:
            L3 = 0;
            break;
        case 3:
            L4 = 0;
            break;
        default:
            L1 = L2 = L3 = L4 = 1;
    }
}

void UartInit(void)        //4800bps@12.000MHz
{
    SCON = 0x50;        //8位数据,可变波特率
    AUXR |= 0x01;        //串口1选择定时器2为波特率发生器
    AUXR |= 0x04;        //定时器2时钟为Fosc,即1T
    T2L = 0x8F;        //设定定时初值
    T2H = 0xFD;        //设定定时初值
    AUXR |= 0x10;        //启动定时器2
}

void SendData(u8 data_buf) //发送一个字符
{
    SBUF = data_buf;
    while(!TI);//TI是发送成功标志
    TI = 0;
}

void Timer0Init(void)        //1毫秒@12.000MHz
{
    AUXR |= 0x80;        //定时器时钟1T模式
    TMOD &= 0xF0;        //设置定时器模式
    TL0 = 0x20;        //设置定时初值
    TH0 = 0xD1;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0 = 1;
}

void T0() interrupt 1{
    second ++;
    cnt ++;
    
    if(cnt > 3){
        cnt = 0;
        dp = Dig[TempVal[LedNum]];
        if(LedNum == 1) dp |= 0x80;
        showNum(dp,LedNum);
        LedNum ++ ;
        LedNum &= 0x03;
    }
}


51-STC / 评 论 (0) / 热度 (379℃) / 2017-03-19 / 阅读全文  / MaWei

NE555振荡电路原理

标 签:

555振荡电路原理图一:

2017031120233016697.png

这已经是第三篇教程了。555的基本原理就不再说了,那直接说这张振荡电路的原理了。其实这个图还是跟上面那个闪光电路是一样的,也是2、6脚的高低电平控制3脚的输出高低电平。

其实上篇的闪光也是一个振荡器,只不过是频率特别低。我们所说的振荡器,一般都是频率比较高的,比例在1KHz以上的。

如果上篇学的很透的话,这个电路也就明白了,就是C1充放电的时间非常短,短到一秒钟充、放电几千,几万,几十万次,就是一个高频振荡器。

当然了,R1的电阻不要太小了,因为太小了,电流会很大,555芯片会烧掉。

知道是什么原理不,因为3脚和7脚的电平状态是一样的。当3脚为低的时候,7脚也为低,所以电流会经过R1从7脚进入555芯片,所以555会因为进入7脚大电流烧掉。

所以一般还是调节R2、C的大小,来控制555芯片。

如果是这个图,可以用下面的公式计算: T=0.7 x (R1 + 2 x R2) x C

由于频率 F 等于周期的倒数,所以又等于 F=1/T=1.43/[(R1+2R2)C],注意的是 C 的单位是 F(法拉),1F = 1 x 10^6uF = 1 x 10^9nF = 1 x 10^12pF

为了振荡器的稳定,一般在脚接一个0.0uF的电容到地。

以上内容参考@浅雪的《学会使用NE555》

电子基础 / 评 论 (0) / 热度 (424℃) / 2017-03-12 / 阅读全文  / MaWei