小结:
1.作者的实验设备完全基于STC89C52RC系列单片机;
2.单片机上阻值102表示10 00欧(2就是两个零);
3.单片机上的电容104表示10 0000,既100kpF=100nF
F->mF->uF->nF->pF
4.单片机IO口是一种弱上拉,强下拉的模式;
一个非常资深的单片机工程师可能需要掌握这些技能
目录
一、LED
1.LED结构
2.LED闪烁
**1.延时函数delay
第一种:
实际测试:延时1ms实际延时0.56ms左右;
void delay(unsigned int xms)
{
unsigned char a,b;
for(;xms>0;xms--);
for(a=38;a>0;a--);
for(b=13;b>0;b--);
}
第二种:
实际测试:延时1ms实际延时1.298ms左右;
void delay(unsigned int xms)
{
unsigned int c;
while(ms--)
{
for(c=114;c>0;c--);
}
}
第三种(ISP生成):
实际测试:延时1ms实际延时1.01ms左右;
void delay() //@12.000MHz 1ms
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
第四种(b站江科大同款):
实际测试:延时1ms实际延时1.033ms左右;
void delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
**2.结构
led高电平点亮,低电平截止。
**3.完整代码
#include <STC89C5xRC.H>
void delay(unsigned int ms)
{
unsigned int c;
while(ms--)
{
for(c=114;c>0;c--);
}
}
void main()
{
while(1)
{
delay(500);
P1=0x7D;
delay(500);
P1=0;
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
3.流水灯
该模块主要是运用:
<<(位左移一位)
>>(位右移一位)
&(位与)
|(位或)
嵌套循环
来实现
**1.语法
**2.完整代码
#include <STC89C5xRC.H>
void delay(unsigned int c)
{
unsigned char a,b;
for(;c>0;c--)
for(b=38;b>0;b--)
for(a=13;a>0;a--);
}
void main ()
{
while(1)
{
int i,j,k,m;
for(i=0;i<2;i++) //左右移位两次
{
for(k=0;k<8;k++)
{
delay(50);
P0=0x01<<k;
}
for(k=0;k<8;k++)
{
delay(50);
P0=0x80>>k;
}
}
P0=0x00; //熄灭缓冲
delay(20);
for(i=0;i<2;i++) //左右递增显示两次
{
for(k=0;k<8;k++)
{
delay(50);
j=0xFE;
P0=~(j<<k);
}
for(k=0;k<8;k++)
{
delay(50);
j=0x7F;
P0=~(j>>k);
}
}
P0=0x00; //熄灭缓冲
delay(20);
for(i=0;i<2;i++) //单LED中间扩散循环两次
{
for(k=0;k<4;k++)
{
delay(50);
j=0x10<<k;
m=0x08>>k;
P0=j|m;
}
for(k=0;k<4;k++)
{
delay(50);
j=0x80>>k;
m=0x01<<k;
P0=j|m;
}
}
P0=0x00; //熄灭缓冲
delay(20);
for(i=0;i<2;i++) //连带LED中间扩散循环两次
{
for(k=0;k<4;k++)
{
delay(500);
j=(j<<k)|0x10;
m=(m>>k)|0x08;
P0=j|m;
}
for(k=0;k<4;k++)
{
delay(500);
j=0xFF>>k;
m=0xFF<<k;
P0=j&m;
}
}
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
二、按键Key
1.结构
2.按键消抖
**1.原理:
按键按下的一瞬间,是一个不规则的连续变化,是一个变化区间,称为抖动;
我们可以用一个循环来检测按键是否按下或者松开;
以下实现按一次亮,按一次灭;
**2.完整代码
#include <STC89C5xRC.H>
void delay(unsigned int ms)
{
unsigned int c;
while(ms--)
{
for(c=114;c>0;c--);
}
}
void main()
{
unsigned char k=0; //无符号字符长度为一个字节,刚好为255
P20=0x01; //初始化引脚
while(1)
{
if(P20==0) //判断按键是否按下
{
delay(20);
while(P20==0)
{
delay(20);
} //跳出此循环意味着按键已经松开
k++;
}
if(k>255)
k=0;
if(k%2==0)
{
P0=0;
}
if(k%2==1)
{
//P0=0x7D; //数字6
P0=k; //输出k
}
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
3.独立按键封装
#include <STC89C5xRC.H>
//#include "onekey.h"
//#include "jtsmg.h"
/***************************************************/
void delay(unsigned int xms)
{
unsigned char i,j;
while(xms--)
{
i=2;
j=239;
do
{
while(--j);
}while(--i);
}
}
/***************************************************/
#define Key P2 //自定义接口
unsigned char onekey() //独立按键模块
{
unsigned char num=0;
Key=0xFF;
while(Key!=0xFF)
{
if(Key==0xFE){while(Key==0xFE);delay(20);num=1;break;}
if(Key==0xFD){while(Key==0xFD);delay(20);num=2;break;}
if(Key==0xFB){while(Key==0xFB);delay(20);num=3;break;}
if(Key==0xF7){while(Key==0xF7);delay(20);num=4;break;}
if(Key==0xEF){while(Key==0xEF);delay(20);num=5;break;}
if(Key==0xDF){while(Key==0xDF);delay(20);num=6;break;}
if(Key==0xBF){while(Key==0xBF);delay(20);num=7;break;}
if(Key==0x7F){while(Key==0x7F);delay(20);num=8;break;}
}
Key=0xFF;
return num;
}
/***************************************************/
#define smg P0 //自定义接口
void jtsmg(unsigned char num) //静态数码管模块
{
unsigned char SEG_NUM[16]={0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6,0xA1, 0x86, 0x8E};
smg=SEG_NUM[num];
}
/***************************************************/
void main()
{
unsigned char keynum;
P0=0xC0;
while(1)
{
keynum=onekey();
if(keynum)
{
jtsmg(keynum);
}
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
三、数码管
1.静态数码管原理
**1.结构
**2.引脚
2.动态数码管原理
**1.结构
动态既指数码管是每时每刻动态刷新的,在一个时刻只能显示一个数码管(或同时显示多个但撒内容相同),所以显示原理就是用人肉眼不可见的高频率对每一个数码管按顺序依次刷新,出现视觉暂留。
利用74HC138译码器来控制段选;
C B A:C是高位,
74HC245 :数据缓冲器,提高驱动能力,可实现控制数据的读取方向
**2.点亮一个指定数码管
第二个数码管显示:6
(段选CBA=001,十进制为1,既第二个Y1输出为1,其余为0,但是输出结果会全部取反,所以输出端为1111 1101,所以Y1对应的既第二个数码管点亮)
#include <STC89C5xRC.H>
void main()
{
while(1)
{
P23=1;//A低位
P22=0;//B
P21=0;//C是高位
P1=0x7D;//共阳极
}
}
**3.同时点亮多个数码管
借鉴上面的经验,复制三个就好了,再加上延时函数;
完整代码:
#include <STC89C5xRC.H>
void delay(unsigned int ms)
{
unsigned int c;
while(ms--)
{
for(c=114;c>0;c--);
}
}
void main()
{
while(1)
{
P23=0;//A低位
P22=0;//B
P21=0;//C是高位
P1=0x7D;//共阳极
P23=1;//A低位
P22=0;//B
P21=0;//C是高位
P1=0x7D;//共阳极
P23=0;//A低位
P22=1;//B
P21=0;//C是高位
P1=0x7D;//共阳极
delay(1000);
P23=0;//A低位
P22=1;//B
P21=0;//C是高位
P1=0x4F;//共阳极
delay(1000);
P23=1;//A低位
P22=0;//B
P21=0;//C是高位
P1=0x5B;//共阳极
delay(1000);
P23=0;//A低位
P22=0;//B
P21=0;//C是高位
P1=0x06;//共阳极
P0=0x82;
delay(1000);
P0=0xff;
delay(1000);
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
**4.封装一个数码管显示函数(附两种数组)
共阳极(高电平点亮):
unsigned char a[17]={0x3F,0x06,0x5B, 0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F, 0x77,0x7C,0x39,0x5E,0x79,0x71,0x00}; //0~F,最后一个为空
共阴极(低电平点亮):
unsigned char SEG_NUM[16]={0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E}; //0~F
代码:
#include <STC89C5xRC.H>
#define C P21
#define B P22
#define A P23
/***************************************************/
void delay(unsigned int ms)
{
unsigned int c;
while(ms--)
{
for(c=114;c>0;c--);
}
}
/***************************************************/
void smg(int w,unsigned int s)
{
unsigned char a[17]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00}; //0~F,最后一个为空
switch(w) //选择位置
{
case 1:C=0;B=0;A=0;break;
case 2:C=0;B=0;A=1;break;
case 3:C=0;B=1;A=0;break;
case 4:C=0;B=1;A=1;break;
case 5:C=1;B=0;A=0;break;
case 6:C=1;B=0;A=1;break;
case 7:C=1;B=1;A=0;break;
case 8:C=1;B=1;A=1;break;
}
P1=0;
P1=a[s];
}
/***************************************************/
void main()
{
while(1)
{
smg(1,1);
delay(1); //为了消影
smg(3,3);
delay(1);
smg(5,5);
delay(1);
smg(7,7);
delay(1);
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
四、模块化编程
1.语法
2.个人经验
1.C文件里定义的函数一定要在头文件中声明;
2.头文件格式#ifndef #define #endif
3.当多个函数需要公用一个变量时,可正常在产生变量的C文件里 正常初始化,然后在外部需要调用其变量的C文件中用 extern +初始化 即可。
五、LCD1602
1.结构电路
2.代码
添加LCD的.h文件和.c文件;
六、矩阵键盘
1.结构
2.原理
和数码管的显示原理一样,都是快速扫描从而实现读取(注意按键消抖);
建议采用逐列扫描,因为单片机IO口冲突,逐行扫描会导致蜂鸣器响;
3.代码
**1.普通十六位扫描法
unsigned char key44() { unsigned char kn=-1; P0=0xFF; P04=0; if(P00==0){delay(20);while(P00==0);delay(20);kn=0;} if(P01==0){delay(20);while(P01==0);delay(20);kn=4;} if(P02==0){delay(20);while(P02==0);delay(20);kn=8;} if(P03==0){delay(20);while(P03==0);delay(20);kn=12;} P0=0xFF; P05=0; if(P00==0){delay(20);while(P00==0);delay(20);kn=1;} if(P01==0){delay(20);while(P01==0);delay(20);kn=5;} if(P02==0){delay(20);while(P02==0);delay(20);kn=9;} if(P03==0){delay(20);while(P03==0);delay(20);kn=13;} P0=0xFF; P06=0; if(P00==0){delay(20);while(P00==0);delay(20);kn=2;} if(P01==0){delay(20);while(P01==0);delay(20);kn=6;} if(P02==0){delay(20);while(P02==0);delay(20);kn=10;} if(P03==0){delay(20);while(P03==0);delay(20);kn=14;} P0=0xFF; P07=0; if(P00==0){delay(20);while(P00==0);delay(20);kn=3;} if(P01==0){delay(20);while(P01==0);delay(20);kn=7;} if(P02==0){delay(20);while(P02==0);delay(20);kn=11;} if(P03==0){delay(20);while(P03==0);delay(20);kn=15;} return kn; } /*********************阿布君***********************/ /****************编码不易,谢谢关注*****************/ /****************QQ:2062808868********************/
**2.十字定位法(要注意高低位!)
------>以下方法为H1作为低位:
#include <STC89C5xRC.H> #include"delay.h" unsigned char SEG_NUM[16]={0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6,0xA1, 0x86, 0x8E}; //0~F void main() { char temp = 0x0; while(1) { P3 = 0x0f; if(P3 != 0x0f) //有按键按下 { delay(10); if(P3 != 0x0f) //再判断一次,确保有按键按下 { temp = P3; //记录当前状态,记录行 P3 = 0xf0; delay(10); switch(P3 | temp) { case 0x77: P0 = SEG_NUM[0]; break; case 0x7b: P0 = SEG_NUM[1]; break; case 0x7d: P0 = SEG_NUM[2]; break; case 0x7e: P0 = SEG_NUM[3]; break; case 0xb7: P0 = SEG_NUM[4]; break; case 0xbb: P0 = SEG_NUM[5]; break; case 0xbd: P0 = SEG_NUM[6]; break; case 0xbe: P0 = SEG_NUM[7]; break; case 0xd7: P0 = SEG_NUM[8]; break; case 0xdb: P0 = SEG_NUM[9]; break; case 0xdd: P0 = SEG_NUM[10]; break; case 0xde: P0 = SEG_NUM[11]; break; case 0xe7: P0 = SEG_NUM[12]; break; case 0xeb: P0 = SEG_NUM[13]; break; case 0xed: P0 = SEG_NUM[14]; break; case 0xee: P0 = SEG_NUM[15]; break; } } } } } /*********************阿布君***********************/ /****************编码不易,谢谢关注*****************/ /****************QQ:2062808868********************/
**3.十字法原理:
区别仅在于读数方向不一样
七、定时器与中断
1.结构逻辑
**1.定时器模式
定时器只配置两个寄存器:TMOD、TCON
而
TMOD 不可位寻址,必须整体赋值;
TCON 可位寻址,可对其中的一位单独赋值,如TF0=0,也可整体;
**1. 时钟频率12Mhz;
**2.中断系统
程序根据中断优先级去执行中断服务,执行完之后程序返回,继续顺序执行下面的代码;
暂停一个中断而去执行其中断,这就是中断嵌套;
2.配置寄存器
**1.配置定时器
**2.配置中断
TMOD 不可位寻址,必须整体赋值;
TCON 可位寻址,可对其中的一位单独赋值,如TF=0,也可整体;
**3.定时器实现LED闪烁(代码)
实现数字6间隔一秒闪烁
#include <STC89C5xRC.H>
#include"delay.h"
/***************************************************/
void Timer0_Init()
{
//TMOD=0x01; //配置TMOD(这样会改变其它位)
//改进 与或法
TMOD&=0XF0; //高四位不变,低四位置0
TMOD|=0X01; //高四位不变 ,最低位置1
TF0=0; //配置TCON
TR0=1;
TH0=64535/256; //配置计数器初始值
TL0=64535%256; //配置成1ms
//配置中断
ET0=1;
EA=1;
PT0=0;
}
/***************************************************/
void main()
{
Timer0_Init();
while(1)
{
P0=0XFF;
}
}
/***************************************************/
void Timer_Routine() interrupt 1
{
static unsigned int count=0;
count++;
TH0=64535/256; //配置计数器初始值
TL0=64535%256;
if(count>=1000)
{
P0=0x82;
delay(1000);
count=0;
}
}
/***************************************************/
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
**4.定时器代码收获
1.配置思路就是,先配置定时器的TMOD(可用与或法)以及TCON,再给计数器赋初值,然后配置中断,最后写中断函数;
2.Timer0_Init()、或者 Timer_Routine()都是随便取的名字,不用按照说明去打;
3.比如代码中64535表示us为单位,整除/赋值给高位TH0,取余%给低位TL0;每计满到65535就会转到中断函数去执行中断(代码里是每计满1ms就执行中断,然后我在中断里计数,1ms*1000等于1s,进入函数执行操作);
4.易错点,我代码里计数函数count定义的是unsigned int,而用char就不行,要考虑类型长度;
5.count定义为static,也是一种优化,静态变量,和全局定义一样的效果。
3.定时器闹钟
**1.直接上代码
#include <STC89C5xRC.H>
#include "timer.h"//前面去引用
#include "smg.h" //
#include "delay.h" //
#include "lcd1602.h" //
unsigned char h=0; //小时
unsigned int h1=0;
unsigned char h2=0;
unsigned char m=0; //分钟
unsigned int m1=0;
unsigned char m2=0;
unsigned char s=0; //秒
unsigned int s1=0;
unsigned char s2=0;
void main()
{
timer0_init();
while(1)
{
P20=1;
// smg(1,1); //测试数码管功能
// delay(1);
// smg(3,3);
// delay(1);
// smg(5,5);
// delay(1);
// smg(7,7);
// delay(1);
s1=s/10;
s2=s-s1*10;
if(s>59) //秒钟进位
{
s=0;
m++;
}
m1=m/10;
m2=m-m1*10;
if(m>59) //分钟进位
{
m=0;
h++;
}
h1=h/10;
h2=h-h1*10;
if(h>59)
h=0;
smg(1,h1);
delay(1);
smg(2,h2);
delay(1);
P21=0;P22=1;P23=0;
P1=0;
delay(1);
smg(4,m1);
delay(1);
smg(5,m2);
delay(1);
P21=1;P22=0;P23=1;
P1=0;
delay(1);
smg(7,s1);
delay(1);
smg(8,s2);
delay(1);
//lcd1602显示
//LCD_ShowString(1,1,"HELLO,AlienABJ");
//LCD_ShowNum(2,4,h,2);
//LCD_ShowString(2,6,"--");
//LCD_ShowNum(2,8,m,2);
//LCD_ShowString(2,10,"--");
//LCD_ShowNum(2,12,s,2);
}
}
/***************************************************/
void timer0_routnie() interrupt 1
{
static unsigned int T0count=0;
TH0=0XFC;
TL0=0X18;
T0count++;
if(T0count==1000) //1000*1ms=1s
{
P21=0;P22=1;P23=0;
P1=0x09;
delay(2);
P21=1;P22=0;P23=1;
P1=0x09;
delay(2);
s++;
T0count=0;
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
**2.模块化定时器代码
#include <STC89C5xRC.H>
void timer0_init()
{
TMOD&=0XF0;
TMOD|=0X01;
TF0=0;
TR0=1;
TH0=0XFC;
TL0=0X18;
ET0=1;
EA=1;
PT0=0;
}
/*中断函数的模板
void timer0_routnie() interrupt 1
{
static unsigned int T0count=0;
TH0=0XFC;
TL0=0X18;
T0count++;
if(T0count==)
{
T0count=0;
}
}
*/
8.串口通信
1.结构原理
**1.介绍
**2.线路连接
**3.工作模式
同步是双方按照同一个时间频率(波特率)采样,用一根线连接;
异步是双方不一样。
**4.串口和中断
**5.视频链接:
[8-1] 串口通信
2.串口(寄存器)的配置
**1.实现单片机发送到电脑(TXD)
#include <STC89C5xRC.H>
unsigned char sn=0;
/***************************************************/
void delay(unsigned int ms)
{
unsigned int c;
while(ms--)
{
for(c=114;c>0;c--);
}
}
/***************************************************/
void uart_TXDinit()
{
//首先配置串口
SCON = 0x40; //工作方式1(可位寻址)这里的REN我设为0,禁止串行接收
PCON |=0x80; //(不可位寻址)波特率加倍是为了降低误差
//接下来配置定时器1为其提供波特率//采用8位自动重载是因为精度高
TMOD &= 0X0F;
TMOD |= 0X20;
TH1=0XF3;
TL1=0XF3;
TR1=1; //TCON配置开始计数
//关闭T1中断
ET1=0;
}
/***************************************************/
void sendinf(unsigned char s)
{
SBUF=s;
while(T1==0); //注意是ti哦,一定要判断标志位
T1=0; //说明手册明确必须软件置0
}
/***************************************************/
void main()
{
uart_TXDinit();
while(1)
{
sendinf(sn);
sn++;
delay(10);
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
**2.实现电脑控制LED并反发回给电脑(RXD)
因为系统不知道何时接收到数据,所以需要中断服务;
#include <STC89C5xRC.H>
/***************************************************/
void uart_RXDinit()
{
//首先配置串口
SCON = 0x50; //工作方式1(可位寻址)这里的REN我设为1,允许接收数据
PCON |=0x80; //(不可位寻址)波特率加倍是为了降低误差
//接下来配置定时器1为其提供波特率//采用8位自动重载是因为精度高
TMOD &= 0X0F;
TMOD |= 0X20;
TH1=0XF3;
TL1=0XF3;
TR1=1; //TCON配置开始计数
//关闭T1中断
ET1=0;
//打开串口接收中断
EA=1; //因为系统不知道何时接收到数据,所以需要中断服务
ES=1;
}
void uart_send(unsigned char inf)
{
SBUF=inf;
while(TI==0);
TI=0;
}
void uart_routine() interrupt 4
{
if(RI==1)
{
P0=SBUF;
uart_send(SBUF);
RI=0;
}
}
/***************************************************/
void main()
{
uart_RXDinit();
while(1)
{
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
**3.波特率的计算方法
**4.Hex模式和文本模式
按照ASCII码转换;
9.LED点阵屏
1.结构原理
**1.引脚
**2.控制原理(74HC595)
采用74HC595串行输出,可同时输出8位不同数据(74HC138译码器只能8选1输出),当多个74HC595级联,可实现IO扩展;
2.配置代码
**1.16*16LED资料
由于我学习的开发板是A7的,是16*16LED点阵屏;使用了4个74HC595;
** 模块收获:
该模块是独立的;
数据0111存入SER的顺序如图:
**2.实现全亮(代码)
#include <STC89C5xRC.H>
sbit SER = P0^0; //SER存入的顺序是数据的高位一位一位的依次存入
sbit SRCK = P0^1;
sbit RCK = P0^2; //RCLK,改名是定义冲突
/***************************************************/
void _74HC595_write(unsigned int inf)
{
unsigned char i;
unsigned int inf1,inf2,inf3; //存储位移后的输入数据
for(i=0;i<8;i++) //最终存入A(控制前8行)
{
SER=(inf<<i);
SRCK=1; //将左移后的数据的第一位存入
SRCK=0;
}
RCK=1; //将数据移入寄存器
RCK=0;
inf1=inf<<8;
for(i=0;i<8;i++) //最终存入B(控制后8行)
{
SER=(inf1<<i);
SRCK=1;
SRCK=0;
}
RCK=1;
RCK=0;
inf2=inf1<<8;
for(i=0;i<8;i++) //最终存入C(控制前8列)
{
SER=(inf2<<i);
SRCK=1;
SRCK=0;
}
RCK=1;
RCK=0;
inf3=inf2<<8;
for(i=0;i<8;i++) //最终存入D(控制后8列)
{
SER=(inf3<<i);
SRCK=1;
SRCK=0;
}
RCK=1;
RCK=0;
}
/***************************************************/
void main()
{
while(1)
{
_74HC595_write(00000000000000001111111111111111); //注意极性
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
**3.任意点亮
16*16我还没搞明白,和8*8原理不一样,还得看视频;
[9-2] LED点阵屏显示图形&动画
**4.画爱心(16*16)
#include <STC89C5xRC.H>
#include "16LED.h"
#include "delay.h" //延用之前的库函数
void main()
{
unsigned int b,a;
int i=0;
unsigned int c[16]={0xFF,0xFF,0xFF,0xFF,0xE7,0xDB,0xBD,0x7D,0xFD,0xFD,0xFB,0xE7,0xEF,0xDF,0xBF,0x7F};
unsigned int d[16]={0xFF,0xFF,0xFF,0xFF,0xE7,0xDB,0xBD,0xBE,0xBF,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0xFE};
//d= 0X00;
//c= 0X00;
b= 0xFF;
a= 0xF0;
LED_init();
while(1)
{
for(;i<16;i++)
{
_74HC595_write(d[i],c[i],b,a);
delay(100);
}
i=0;
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
**5.实现滚屏效果
实现滚屏效果,就是定义一个数组,一个画面扫描特定次数(帧)后,顺序输入数组接下来的数据,如图:
当然有专用软件去实现:文字取模软件
**6.待解决问题
16*16LED点阵屏如何精确控制?
10.DS1302实时时钟
1.结构原理
**1.引脚介绍
单片机定时器做时钟不精准;
2.代码配置
**1.实现数据读写(注意事项)
注意:
1.单片机与DS1302交互口为
DS1302_CE //使能端
DS1302_IO //传送数据
DS1302_SCLK //脉冲信号2.DS1302_IO每次发送信号都是先发送命令字(先发送低位),要先触发才会读取数据;
3.要注意区别read和write两个过程;
4.如果读出数据大于59且长时间不动,则可能开了读写保护,DS1302_write(0x8E,0x00)向WP地址(0x8E)写入低电平;
5.注意DS1302识别为BCD码:
**2.代码(简单实现):
#include <STC89C5xRC.H>
#include "lcd1602.h"
#include "DS1302.h" //延用之前的库函数
/***************************************************/
void main()
{
unsigned char s,m,h;
LCD_Init();
DS1302_init();
//DS1302_write(0X8E,0x00); //解除写保护
DS1302_write(0X80,0X10); //s//0X80处是write
DS1302_write(0X82,0X58); //m//注意DS1302识别为BCD码
DS1302_write(0X84,0X19); //h
while(1)
{
LCD_ShowString(1,1,"AB'Clock:");
s = DS1302_read(0x81);
m = DS1302_read(0x83);
h = DS1302_read(0x85);
LCD_ShowString(2,1,"h:");
LCD_ShowHexNum(2,3,h,2);
LCD_ShowString(2,5,"m:");
LCD_ShowHexNum(2,7,m,2);
LCD_ShowString(2,9,"s:");
LCD_ShowHexNum(2,11,s,2);
}
}
//*****************************************以下是DS1302.h库函数
sbit DS1302_CE = P2^7; //使能端
sbit DS1302_IO = P2^6; //传送数据
sbit DS1302_SCLK = P2^5; //脉冲信号
void DS1302_init() //初始化
{
DS1302_CE = 0;
DS1302_SCLK = 0;
}
void DS1302_write(unsigned char command,unsigned char Data) //写入函数
{
unsigned char i;
DS1302_CE = 1;
for(i=0;i<8;i++) //写入命令字
{
DS1302_IO = command & (0x01<<i);
DS1302_SCLK = 1; //注意这里因为整个过程都是上升沿触发,所以先1后0
DS1302_SCLK = 0;
}
for(i=0;i<8;i++) //写入数据
{
DS1302_IO = Data & (0x01<<i);
DS1302_SCLK = 1;
DS1302_SCLK = 0;
}
DS1302_CE = 0;
}
unsigned char DS1302_read(unsigned char command)
{
unsigned char i,Data=0x00;
//command|=0x01; //将指令转换为读指令
DS1302_CE = 1;
for(i=0;i<8;i++) //写入命令字
{
DS1302_IO = command & (0x01<<i);
DS1302_SCLK = 0; //注意这里是前一个命令字是上升沿触发,后部分是下降沿触发,所以先0后1
DS1302_SCLK = 1;
}
for(i=0;i<8;i++) //读出数据到Data
{
DS1302_SCLK = 1;
DS1302_SCLK = 0;
if(DS1302_IO) //**注意这里的顺序,因为是read,原理是当一个下降沿触发后单片机才会去读入一个数据!
Data|=(0x01<<i);
}
DS1302_CE = 0;
DS1302_IO = 0;
return Data;
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
后续按键修改时间的方法见视频:
[10-2] DS1302时钟&可调时钟
**3.待解决问题:
再次接电时间会从初始值开始,怎么避免?
11.蜂鸣器
1.引脚原理
**1.引脚
**2.原理
通过三极管(PNP、NPN)来驱动,IO口低电平驱动(有源);
声乐学习:中央C,低八度、高八度......
视频:
[10-2] DS1302时钟&可调时钟
2.代码(两只老虎)
#include <STC89C5xRC.H>
sbit BUZZ = P1^6; //蜂鸣器控制引脚
unsigned int code NoteFrequ[] = { //中音 1-7 和高音 1-7 对应频率列表
523, 587, 659, 698, 784, 880, 988, //中音 1-7
1047, 1175, 1319, 1397, 1568, 1760, 1976 //高音 1-7
};
unsigned int code NoteReload[] = { //中音 1-7 和高音 1-7 对应的定时器重载值
65536 - (11059200/12) / (523*2), //中音 1
65536 - (11059200/12) / (587*2), //2
65536 - (11059200/12) / (659*2), //3
65536 - (11059200/12) / (698*2), //4
65536 - (11059200/12) / (784*2), //5
65536 - (11059200/12) / (880*2), //6
65536 - (11059200/12) / (988*2), //7
65536 - (11059200/12) / (1047*2), //高音 1
65536 - (11059200/12) / (1175*2), //2
65536 - (11059200/12) / (1319*2), //3
65536 - (11059200/12) / (1397*2), //4
65536 - (11059200/12) / (1568*2), //5
65536 - (11059200/12) / (1760*2), //6
65536 - (11059200/12) / (1976*2), //7
};
bit enable = 1; //蜂鸣器发声使能标志
bit tmrflag = 0; //定时器中断完成标志
unsigned char T0RH = 0xFF; //T0 重载值的高字节
unsigned char T0RL = 0x00; //T0 重载值的低字节
void PlayTwoTiger();
void main()
{
unsigned int i;
EA = 1; //使能全局中断
TMOD = 0x01; //配置 T0 工作在模式 1
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1; //使能 T0 中断
TR0 = 1; //启动 T0
while (1)
{
PlayTwoTiger(); //播放乐曲--两支老虎
for (i=0; i<40000; i++); //停止一段时间
}
}
/* 两只老虎乐曲播放函数 */
void PlayTwoTiger()
{
unsigned char beat; //当前节拍索引
unsigned char note; //当前节拍对应的音符
unsigned int time = 0; //当前节拍计时
unsigned int beatTime = 0; //当前节拍总时间
unsigned int soundTime = 0; //当前节拍需发声时间
//两只老虎音符表
unsigned char code TwoTigerNote[] = {
1, 2, 3, 1, 1, 2, 3, 1, 3, 4, 5, 3, 4, 5,
5,6, 5,4, 3, 1, 5,6, 5,4, 3, 1, 1, 5, 1, 1, 5, 1,
};
//两只老虎节拍表,4 表示一拍,1 就是 1/4 拍,8 就是 2 拍
unsigned char code TwoTigerBeat[] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 8,
3,1, 3,1, 4, 4, 3,1, 3,1, 4, 4, 4, 4, 8, 4, 4, 8,
};
for (beat=0; beat<sizeof(TwoTigerNote); ) //用节拍索引作为循环变量
{
while (!tmrflag); //每次定时器中断完成后,检测并处理节拍
tmrflag = 0;
if (time == 0) //当前节拍播完则启动一个新节拍
{
note = TwoTigerNote[beat] - 1;
T0RH = NoteReload[note] >> 8;
T0RL = NoteReload[note];
//计算节拍总时间,右移 2 位相当于除 4,移位代替除法可以加快执行速度
beatTime = (TwoTigerBeat[beat] * NoteFrequ[note]) >> 2;
//计算发声时间,为总时间的 0.75,移位原理同上
soundTime = beatTime - (beatTime >> 2);
enable = 1; //指示蜂鸣器开始发声
time++;
}
else //当前节拍未播完则处理当前节拍
{
if (time >= beatTime) //当前持续时间到达节拍总时间时归零,
{ //并递增节拍索引,以准备启动新节拍
time = 0;
beat++;
}
else //当前持续时间未达到总时间时,
{
time++; //累加时间计数
if (time == soundTime) //到达发声时间后,指示关闭蜂鸣器,
{ //插入 0.25*总时间的静音间隔,
enable = 0; //用以区分连续的两个节拍
}
}
}
}
}
/* T0 中断服务函数,用于控制蜂鸣器发声 */
void InterruptTimer0() interrupt 1
{
TH0 = T0RH; //重新加载重载值
TL0 = T0RL;
tmrflag = 1;
if (enable) //使能时反转蜂鸣器控制电平
BUZZ = ~BUZZ;
else //未使能时关闭蜂鸣器
BUZZ = 1;
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
12.LED呼吸灯&直流电机(PWM脉冲宽度调制)
1.硬件原理
**1.引脚芯片
**2.原理
通过改变占空比调节速度;
2.代码配置
**1.LED呼吸灯
#include <STC89C5xRC.H>
#include "delay.h"
#define T 100 //周期
#define step 20 //步频,相当于变化过程的快慢,值越大,变化过程越平缓,变慢
#define LED P0 //低电平点亮
unsigned int i,j;
/***************************************************/
void main()
{
while(1)
{
LED=0xFF;
for(i=0;i<T;i++) //由暗到亮
{
for(j=0;j<step;j++)
{
LED=0xFF; //灭
delay(T-i); //时间长
LED=0; //亮
delay(i); //时间短
}
}
for(i=T;i>0;i--) //由亮到暗
{
for(j=step;j>0;j--)
{
LED=0xFF;
delay(T-i);
LED=0;
delay(i);
}
}
LED=0xFF;
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
**2.直流电机
#include <STC89C5xRC.H>
//#include "jtsmg.h"
//#include "onekey.h"
//#include "timer.h"
#define maxspeed 60 //实测它并不能改变最大速度为多少,它的值越小越好,*
unsigned int speed; //*这样电机抖动更小更丝滑,大于250电机就会怠速,越小越密但是要考虑电机启动力
sbit motor=P1^0; //NLU2003芯片输入端
/***************************************************/
void delay(unsigned int ms)
{
unsigned int c;
while(ms--)
{
for(c=114;c>0;c--);
}
}
/***************************************************/
#include <STC89C5xRC.H>
#include "delay.h"
void jtsmg(unsigned char num)
{
unsigned char SEG_NUM[16]={0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6,0xA1, 0x86, 0x8E};
P0=SEG_NUM[num];
}
/***************************************************/
#include <STC89C5xRC.H>
#include "delay.h"
unsigned char onekey() //独立按键模块
{
unsigned char num=0;
P3=0xFF;
while(P3!=0xFF)
{
if(P3==0xFE){while(P3==0xFE);delay(20);num=1;break;}
if(P3==0xFD){while(P3==0xFD);delay(20);num=2;break;}
if(P3==0xFB){while(P3==0xFB);delay(20);num=3;break;}
if(P3==0xF7){while(P3==0xF7);delay(20);num=4;break;}
if(P3==0xEF){while(P3==0xEF);delay(20);num=5;break;}
if(P3==0xDF){while(P3==0xDF);delay(20);num=6;break;}
if(P3==0xBF){while(P3==0xBF);delay(20);num=7;break;}
if(P3==0x7F){while(P3==0x7F);delay(20);num=8;break;}
}
P3=0xFF;
return num;
}
/***************************************************/
#include <STC89C5xRC.H>
void timer0_init() //100微秒@12.000MHz
{
TMOD&=0XF0;
TMOD|=0X01;
TF0=0;
TR0=1;
// TH0=0XFC; //这是1ms
// TL0=0X18;
TL0 = 0x9C; //设置定时初始值
TH0 = 0xFF; //设置定时初始值
ET0=1;
EA=1;
PT0=0;
}
/***************************************************/
void main()
{
unsigned char keynum;
timer0_init();
motor=0;
while(1)
{
keynum=onekey();
if(keynum)
{
switch(keynum) //设置速度比例
{
case 1:speed=0;jtsmg(keynum);break; //停止
case 2:speed=maxspeed/4;jtsmg(keynum);break;
case 3:speed=maxspeed/2;jtsmg(keynum);break;
case 4:speed=(maxspeed*0.8);jtsmg(keynum);break;
case 5:speed=maxspeed;jtsmg(keynum);break; //全速
}
}
}
}
/***************************************************/
void timer0_routnie() interrupt 1
{
static unsigned int T0count=0;
// TH0=0XFC;
// TL0=0X18;
TL0 = 0x9C; //设置定时初始值
TH0 = 0xFF; //设置定时初始值
T0count++;
T0count%=maxspeed; //T0count不会大于99;这样写简略,省掉了判断语句;
if(T0count<speed)
{
motor=1; //转动
}
else
{
motor=0; //停止
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
13.AD/DA(数模/模数转换)
1.结构原理
**1.引脚
目前开发板用的都是触摸屏芯片(触摸屏原理也是如此);
以下是以前的老芯片:
**2.介绍
**3.相关原理
2.代码配置
**1.AD 模数转换
需要配置一个读函数,和DS1302时钟芯片一样需要先给一个命令字,同时使用上升沿触发;
以下是配置命令字的规范(表5):
依次测量Xp, Vbat, Yp, AUX脚的电压值。实际电路中它们分别与电位器、光敏电阻、热敏电阻、DAC1相连。
因此我们控制时钟的同时去读出AD值就好了;
#include <STC89C5xRC.H>
//#include "delay.h"
//#include "lcd1602.h"
//#include "AD.h"
//定义外部引脚 //实际测试发现,P0口和P2口不能用!
sbit X_DO = P1^7; //输出
sbit X_DI = P1^6; //输入
sbit X_CS = P1^5; //使能端
sbit X_CLK = P1^4; //时钟信号
/***************************************************/
unsigned int XPT2046_readADvalue(unsigned char command)//该芯片不同于DS1302,它是先读入高位
{
unsigned int ADvalue=0;
unsigned char i;
X_CS=0;//
X_CLK=0;//类似于初始化
for(i=0;i<8;i++) //读入命令字
{
X_DI=command&(0x80>>i);
X_CLK=1;
X_CLK=0;
}
// X_CLK=1;//跳过中间的一个空白信号
// X_CLK=0;
for(i=0;i<16;i++)//读出AD值
{
X_CLK=1;
X_CLK=0;
if(X_DO)
ADvalue|=(0x8000>>i);
}
X_CS=1;
return ADvalue>>8;//只显示8位的
}
/***************************************************/
void delay(unsigned int ms)
{
unsigned int c;
while(ms--)
{
for(c=114;c>0;c--);
}
}
/***************************************************/
void main()
{
unsigned int RADvalue,RMvalue,LTvalue;
LCD_Init();
LCD_ShowString(1,1,"RAD RM LT");
while(1)
{
RADvalue=XPT2046_readADvalue(X_XP); //旋钮阻值
LCD_ShowNum(2,1,RADvalue,3);
RMvalue=XPT2046_readADvalue(X_YP); //热敏电阻
LCD_ShowNum(2,6,RMvalue,3);
LTvalue=XPT2046_readADvalue(X_VBAT); //光敏电阻
LCD_ShowNum(2,10,LTvalue,3);
delay(100); //屏幕刷新率
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
**收获:
操作方式和DS1302的差不多,也是在脉冲信号的触发下,先写入命令字,再执行写入或者读出。但是这里要注意引脚问题,该实验实际测试发现,P0口和P2口不能用!
底层原理和电路逻辑还需要更进一步学习!
**2.基于AD转换实现直流电机调速(PWM)
通过读出旋钮的阻值来实时改变电机的转速;
AD模块、定时器模块、LCD模块、延时模块
和上一个代码相比实际上只是将实时变化的AD作为阈值,这样占空比也将实时改变;
#include <STC89C5xRC.H>
//#include "delay.h"
//#include "timer.h"
//#include "lcd1602.h"
//#include "AD.h"
unsigned char speed=253; //最大速度为255(AD值)
unsigned char nowspeed;
//定义外部引脚 //实际测试发现,P0口和P2口不能用!
sbit X_DO = P1^7; //输出
sbit X_DI = P1^6; //输入
sbit X_CS = P1^5; //使能端
sbit X_CLK = P1^4; //时钟信号
/***************************************************/
unsigned int XPT2046_readADvalue(unsigned char command)//该芯片不同于DS1302,它是先读入高位
{
unsigned int ADvalue=0;
unsigned char i;
X_CS=0;//
X_CLK=0;//类似于初始化
for(i=0;i<8;i++) //读入命令字
{
X_DI=command&(0x80>>i);
X_CLK=1;
X_CLK=0;
}
// X_CLK=1;//跳过中间的一个空白信号
// X_CLK=0;
for(i=0;i<16;i++)//读出AD值
{
X_CLK=1;
X_CLK=0;
if(X_DO)
ADvalue|=(0x8000>>i);
}
X_CS=1;
return ADvalue>>8;//只显示8位的
}
/***************************************************/
#include <STC89C5xRC.H>
void timer0_init()
{
TMOD&=0XF0;
TMOD|=0X01;
TF0=0;
TR0=1;
// TH0=0XFC; //这是之前的1ms
// TL0=0X18;
TL0 = 0x9C; //设置定时初始值 //100微秒@12.000MHz
TH0 = 0xFF; //设置定时初始值
ET0=1;
EA=1;
PT0=0;
}
/***************************************************/
void delay(unsigned int ms)
{
unsigned int c;
while(ms--)
{
for(c=114;c>0;c--);
}
}
/***************************************************/
void main()
{
timer0_init();
LCD_Init();
while(1)
{
nowspeed=XPT2046_readADvalue(X_XP); //随时获取当前AD值(实时速度)
if(nowspeed)
{
LCD_ShowString(1,1,"Working Now! ");//上下两句话要等长,才能实现覆盖刷新
}
else
{
LCD_ShowString(1,1,"Don't Working!");
}
LCD_ShowString(2,1,"Speed:");
LCD_ShowNum(2,8,nowspeed,3);
//delay(2000);
}
}
void timer0_routnie() interrupt 1
{
static unsigned int T0count=0;
// TH0=0XFC;
// TL0=0X18;
TL0 = 0x9C; //设置定时初始值
TH0 = 0xFF; //设置定时初始值
T0count++;
T0count%=speed;
if(T0count<=nowspeed&&nowspeed!=0) //这里的!=0是为了防止电机低速抖动,因为nowspeed不可能真正为0(精度问题)
P10=1;
else
P10=0;
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
****
**3.DA模块转换
DA就是一个低通滤波;
#include <STC89C5xRC.H>
//#include "Delay.h"
//#include "Timer0.h"
sbit DA=P2^1;
unsigned char Counter,Compare; //计数值和比较值,用于输出PWM
unsigned char i;
/***************************************************/
/**
* @brief 定时器0初始化,100us@12.000MHz
* @param 无
* @retval 无
*/
void Timer0_Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x9C; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
/***************************************************/
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
/***************************************************/
void main()
{
Timer0_Init();
while(1)
{
for(i=0;i<100;i++)
{
Compare=i; //设置比较值,改变PWM占空比
Delay(10);
}
for(i=100;i>0;i--)
{
Compare=i; //设置比较值,改变PWM占空比
Delay(10);
}
}
}
void Timer0_Routine() interrupt 1
{
TL0 = 0x9C; //设置定时初值
TH0 = 0xFF; //设置定时初值
Counter++;
Counter%=100; //计数值变化范围限制在0~99
if(Counter<Compare) //计数值小于比较值
{
DA=1; //输出1
}
else //计数值大于比较值
{
DA=0; //输出0
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
14.红外遥控(外部中断)
1.结构原理
**1.介绍
**2.电路原理
单片机上的接收模块是高度集成化的,是已经将信号解调后通过out输出的;
主流通信协议NEC编码;
不同于数字型号高电平表示1,低电平表示0,该接收器是通过波的时间区分0或者1的;
示波器显示的out输出的波形:
每个按键都对应不同的键码;
通过外部中断接收,并将中断优先级设置为最高反正信号错过或者延迟;
还得配置寄存器TCON:
2.代码配置(详解)
本代码定义了一个专用红外模块,通过红外模块调用外部中断INT0(检测和接收信号)和定时器0(用来计时)。
红外模块能够读取信号信息的原理通过区分不同波的时间(协议决定,发出和接收一致),该时间问题可通过定时器作为计数器解决;
红外模块主要实现过程分为以下四步骤:
1.先判断是否由信号,有就从空闲状态转为状态1;
2.确认有信号后,通过起始信号时间长短判断是start信号还是repeat信号,并置标志位;
3.通过判断标志位进入状态3,状态3实现后面32位的Data信号解调;
4.一定要验证信号是否有误差,确认无误差后将缓冲区R_Data[4]中数据分别读出到最终的IR_Address(红外地址号)和IR_Command(红外命令数)。
#include <STC89C5xRC.H>
//#include "lcd1602.h"
//#include "delay.h"
//#include "Redcontrol.h" //红外模块
unsigned int num=0;
unsigned char Address=0;
unsigned char Command=0;
/********************************************/
//按键键码
#define IR_POWER 0x45
#define IR_MODE 0x46
#define IR_MUTE 0x47
#define IR_START_STOP 0x44
#define IR_PREVIOUS 0x40
#define IR_NEXT 0x43
#define IR_EQ 0x07
#define IR_VOL_MINUS 0x15
#define IR_VOL_ADD 0x09
#define IR_0 0x16
#define IR_RPT 0x19
#define IR_USD 0x0D
#define IR_1 0x0C
#define IR_2 0x18
#define IR_3 0x5E
#define IR_4 0x08
#define IR_5 0x1C
#define IR_6 0x5A
#define IR_7 0x42
#define IR_8 0x52
#define IR_9 0x4A
/********************************************/
void delay(unsigned int xms) //这种更精确
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
/********************************************/
void INT0_init() //外部中断
{
IE0=0; //清除标志位
IT0=1; //1为下降沿触发,0为低电平触发
EX0=1;
EA=1;
PX0=1;//优先级最好设为最高
}
/********************************************/
//测试发现延时13ms实际13.117ms,14ms实际延时14.124ms
void timer0_init() //这里定时器用作计数器初始化
{
TMOD&=0XF0;
TMOD|=0X01;
TF0=0;
TR0=0; //给0,先禁止计数
// TH0=0XFC; //这是1ms
// TL0=0X18;
// TL0 = 0x9C; //设置定时初始值
// TH0 = 0xFF; //设置定时初始值
TH0=0;
TL0=0;
// ET0=1; //计数器不需要中断
// EA=1;
// PT0=0;
}
void timer0_setnum(unsigned int star)//设置时间起始值
{
TH0=star/256;
TL0=star%256;
}
unsigned int timer0_getnum() //获取当前时间
{
unsigned int timenow;
timenow=TH0*256+TL0;
//timenow=(TH0<<8)|TL0;//两种一样的
return timenow;
}
void timer0_run(unsigned char runFlag) //开启计数
{
TR0=runFlag;
}
/********************************************/
unsigned int IR_Time; //红外接收时间
unsigned char IR_State=0; //红外状态:0空闲 1刚接实秸stat信号 2读取数据
unsigned char IR_Data[4]; //存储data信息(数据缓冲区)
unsigned char IR_pData; //指向每一位数据(1~32)
unsigned char IR_DataFlag; //接收数据标志
unsigned char IR_RepeatFlag; //重发标志
unsigned char IR_Address; //红外地址号
unsigned char IR_Command; //红外命令数
void RI_init() //总初始化
{
INT0_init();
timer0_init();
}
unsigned char getIR_DataFlag() //返回数据状态供主函数判断
{
if(IR_DataFlag)
{
IR_DataFlag=0;
return 1;
}
return 0;
}
unsigned char getIR_RepeatFlag() //返回重发状态供主函数判断
{
if(IR_RepeatFlag)
{
IR_RepeatFlag=0;
return 1;
}
return 0;
}
unsigned char getIR_Address() //返回地址数
{
return IR_Address;
}
unsigned char getIR_Command() //返回命令号
{
return IR_Command;
}
void INT0_routine() interrupt 0 //**中断里执行信号接收与解调**
{
if(IR_State==0) //空闲状态突然接收到信号之后的操作 //---->判断位一
{
timer0_setnum(0);
timer0_run(1);
IR_State=1;
}
else if(IR_State==1) //一有信号就会进入到这里 //---->判断位二
{
IR_Time=timer0_getnum();
timer0_setnum(0); //计数器清零
if(13500-500<IR_Time && IR_Time<13500+500) //判断是否是开始信号,等待start或者repeat
{
IR_State=2; //确定为start信号
}
else if(11250-500<IR_Time && IR_Time<11250+500)
{
IR_State=0; //因为是重复信号,直接从缓冲区IR_Data[4]读取即可
timer0_run(0); //也可以不停
IR_RepeatFlag=1; //接收到repeat信号标志
}
else //判断是否错误信号,是就重新接收信号
{
IR_State=1;
}
}
else if(IR_State==2) //***解码*** //---->判断位三
{
IR_Time=timer0_getnum();
timer0_setnum(0); //计数器清零
if(1120-500<IR_Time && IR_Time<1120+500) //判断是否位0信号
{
IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //注意这里数据是先进地位
IR_pData++;
}
else if(2250-500<IR_Time && IR_Time<2250+500) //判断是否为1信号
{
IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8));
IR_pData++;
}
else
{
IR_State=1;
IR_pData=0; //数据位置指针清0
}
if(IR_pData>=32) //验证过程是否存在误差
{
IR_pData=0;
if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3]))
{
IR_Address=IR_Data[0]; //转存数据
IR_Command=IR_Data[2];
IR_DataFlag=1; //置收到连发帧标志位为1
}
timer0_run(0);
IR_State=0;
}
}
}
/********************************************/
void main()
{
LCD_Init();
RI_init();
LCD_ShowString(1,1,"Add");
LCD_ShowString(1,7,"Com");
LCD_ShowString(1,13,"Num");
while(1)
{
if(getIR_DataFlag() || getIR_RepeatFlag())
{
Address=getIR_Address();
Command=getIR_Command();
if(Command==IR_VOL_MINUS) //如果遥控器VOL-按键按下
{
num--; //Num自减
}
if(Command==IR_VOL_ADD) //如果遥控器VOL+按键按下
{
num++; //Num自增
}
}
LCD_ShowHexNum(2,1,Address,3);
LCD_ShowHexNum(2,7,Command,3);
LCD_ShowNum(2,13,num,3);
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
15.AT24C02(I2C总线)
1.结构原理
**1.引脚
**2.介绍
**3.原理
2.代码配置
**1.收获&理解
和DS1302时钟芯片以及串口的原理类似,都是通过CLK时钟以及一个信号线(因为都是半双工)实现的数据的交换。
实现过程:
1.先建立I2C总线的模块,I2C总线为两个过程:主机发送以及主机接收。第一个过程主机接收从机的应答(0应答,1非应答),第二个过程主机发送给从机应答。
所以I2C模块有:每次开始信号S、结束信号P、发送数据、接收数据、发送应答、接收应答共六个模块。
2.再建立AT24C02模块(调用I2C模块),功能有:Write数据、Read数据。
**特别注意该芯片的写周期为5ms,所以每次写入后应该延时5ms才能读取到数据。
**2.代码1 (初步实现功能)
实现按键1加,2减,3写入,4读出(可掉电保存数据)
#include <STC89C5xRC.H>
//#include "onekey.h"
//#include "delay.h"
//#include "lcd1602.h"
//#include "AT24C02.h"
/***************************************************/
void delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
/***************************************************/
unsigned char onekey() //独立按键模块
{
unsigned char num=0;
Key=0xFF;
while(Key!=0xFF)
{
if(Key==0xFE){while(Key==0xFE);delay(10);num=1;delay(1);break;}
if(Key==0xFD){while(Key==0xFD);delay(10);num=2;delay(1);break;}
if(Key==0xFB){while(Key==0xFB);delay(10);num=3;delay(1);break;}
if(Key==0xF7){while(Key==0xF7);delay(10);num=4;delay(1);break;}
if(Key==0xEF){while(Key==0xEF);delay(10);num=5;delay(1);break;}
if(Key==0xDF){while(Key==0xDF);delay(10);num=6;delay(1);break;}
if(Key==0xBF){while(Key==0xBF);delay(10);num=7;delay(1);break;}
if(Key==0x7F){while(Key==0x7F);delay(10);num=8;delay(1);break;}
}
Key=0xFF;
return num;
}
/***************************************************/
#include <STC89C5xRC.H>
sbit I2C_SCL = P1^0;
sbit I2C_SDA = P1^1;
//主机开始发送数据的起始信号
void I2C_start()
{
I2C_SDA = 1;
I2C_SCL = 1;
I2C_SDA = 0;
I2C_SCL = 0;
}
//主机结束发送数据的结束信号
void I2C_stop()
{
//I2C_SCL = 0;肯定为0的
I2C_SDA = 0;
I2C_SCL = 1;
I2C_SDA = 1;
}
//主机发送数据
void I2C_sendbyte(unsigned char byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
I2C_SDA = byte&(0x80>>i);
I2C_SCL = 1;
I2C_SCL = 0;
}
}
//主机接收数据
unsigned char I2C_receivebyte()
{
unsigned char i,Data;
for(i=0;i<8;i++)
{
if(I2C_SDA){Data|=(0x80>>i);}
I2C_SCL = 1;
I2C_SCL = 0;
}
return Data;
}
//主机发送数据后从机返回应答ACK
unsigned char I2C_receiveACK()
{
unsigned char ack=0;
I2C_SDA = 1;//释放总线,从而让从机用此线发送数据(半双工)
ack = I2C_SDA; //******************************************留意
I2C_SCL = 1;
I2C_SCL = 0;
return ack;
}
//主机每接收一byte后返回给从机的应答ACK
void I2C_sendACK(unsigned char ack)
{
I2C_SDA = ack;
I2C_SCL = 1;
I2C_SCL = 0;
}
/***************************************************/
#define AT24C02_SLAVE_AddressW 0xA0 //写入的地址
#define AT24C02_SLAVE_AddressR 0xA1 //读出的地址
void AT24C02_WriteData(unsigned char WordAddress,Data) //(从机地址+写,写入的数据)
{
unsigned char ack=0; //定义ack可用于接收应答以判定是否接收成功
I2C_start();//必定先发送开始信号
I2C_sendbyte(AT24C02_SLAVE_AddressW);
ack=I2C_receiveACK();
I2C_sendbyte(WordAddress);
ack=I2C_receiveACK();
I2C_sendbyte(Data);
ack=I2C_receiveACK();
I2C_stop();
}
unsigned char AT24C02_ReadData(unsigned char WordAddress)
{
unsigned char Data,ack;
I2C_start();//必定先发送开始信号
I2C_sendbyte(AT24C02_SLAVE_AddressW);
ack=I2C_receiveACK();
I2C_sendbyte(WordAddress);
ack=I2C_receiveACK();
I2C_start();
I2C_sendbyte(AT24C02_SLAVE_AddressR);
ack=I2C_receiveACK();
Data=I2C_receivebyte();
I2C_sendACK(1);
I2C_stop();
return Data;
}
/***************************************************/
void main()
{
unsigned int key,num=0;
LCD_Init();
LCD_ShowNum(1,1,num,5);
while(1)
{
key=onekey();
if(key==1)
{
num++;
LCD_ShowNum(1,1,num,5);
}
if(key==2)
{
num--;
LCD_ShowNum(1,1,num,5);
}
if(key==3)
{
AT24C02_WriteData(0,num%256);
delay(5);
AT24C02_WriteData(1,num/256);
delay(5);
LCD_ShowString(2,1,"Write Done!");
delay(1000);
LCD_ShowString(2,1," ");
}
if(key==4)
{
num=(AT24C02_ReadData(1)<<8)|AT24C02_ReadData(0);
AT24C02_WriteData(1,num/256);
LCD_ShowNum(1,1,num,5);
LCD_ShowString(2,1,"Read Done!");
delay(1000);
LCD_ShowString(2,1," ");
}
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
**3.代码2(秒表)
未完待续...
16.DS18B20温度传感器
1.结构原理
**1.引脚
**2.介绍
**3.原理
2.代码配置
未完待续...
17.继电器模块(HW—803)
1.结构原理
**1.引脚
**2.原理
由继电器的原理可知,继电器本身并没有正负之分,即是说:只要在继电器的线圈上通上电流,就会使线圈产生吸引力,是的公共触点与常开触点相接触。
P1口作为负载端,其中1口为常闭NC,2口为公共端(必须接),3口为常开NO。(1、3为无信号时默认状态)(常接入火线)
J15为信号输入端,由于PNP型三极管发射极接VCC,所以输入低电平信号使能。(具体要看电路)
2.代码配置
**1.代码
独立按键控制实现改变状态(按键音);
#include <STC89C5xRC.H>
//#include "onekey.h"
//#include "jdq.h"
/***************************************************/按键
#include <STC89C5xRC.H>
#include "delay.h"
#include "buzzer.h"
#define Key P2 //ؔ֨ӥޓࠚ
unsigned char onekey() //׀bдݼģࠩ
{
unsigned char num=0;
Key=0xFF;
while(Key!=0xFF)
{
if(Key==0xFE){while(Key==0xFE);delay(10);num=1;buzzerkey_time(10);break;}
if(Key==0xFD){while(Key==0xFD);delay(10);num=2;buzzerkey_time(10);break;}
if(Key==0xFB){while(Key==0xFB);delay(10);num=3;buzzerkey_time(10);break;}
if(Key==0xF7){while(Key==0xF7);delay(10);num=4;buzzerkey_time(10);break;}
if(Key==0xEF){while(Key==0xEF);delay(10);num=5;buzzerkey_time(10);break;}
if(Key==0xDF){while(Key==0xDF);delay(10);num=6;buzzerkey_time(10);break;}
if(Key==0xBF){while(Key==0xBF);delay(10);num=7;buzzerkey_time(10);break;}
if(Key==0x7F){while(Key==0x7F);delay(10);num=8;buzzerkey_time(10);break;}
}
Key=0xFF;
return num;
}
/***************************************************/继电器
#include <STC89C5xRC.H>
sbit jdq=P1^7;
void jdqkz(unsigned char state)
{
jdq=state;
}
/***************************************************/蜂鸣器
#include <STC89C5xRC.H>
#include <INTRINS.H> //Ϊ_nop_گ˽
sbit buzzer = P1^1;
void delay_buzzer500us() //@12.000MHz //buzzerרԃ
{
unsigned char i;
_nop_();
i = 247;
while (--i);
}
void Delaykey50us() //@12.000MHz //дݼ͡ʾӴ
{
unsigned char i;
_nop_();
i = 22;
while (--i);
}
void buzzer_time(unsigned int ms)
{
unsigned int i;
for(i=0;i<ms*2;i++)
{
buzzer=~buzzer;
delay_buzzer500us();
}
}
void buzzerkey_time(unsigned int ms)
{
unsigned int i;
for(i=0;i<ms*2;i++)
{
buzzer=~buzzer;
Delaykey50us();
}
}
/***************************************************/延时函数
void delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
/***************************************************/主函数
unsigned state=0;
void main()
{
//jdqkz(1);
while(1)
{
state=onekey();
if(state==1)
{
jdqkz(1);
}
if(state==2)
{
jdqkz(0);
}
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/
18.超声波模块(HY-SRF05)
1.结构原理
**1.引脚&电气参数
网上没找到其他引脚图片了
该模块一共有5个引脚,分别是VCC、GND、Trig、Echo、Out
其中VCC和GND不多介绍,手册上说VCC需要接5V,但是我看网上很多人都是接的3.3V也可以照常使用。
OUT:该引脚在手册中提到为开关量输出,可以做报警模块使用,根据我的理解,他应该是一个模块状态口,通过判断OUT的高低电平来判断模块是否上电,查阅很多博主的博客发现对该接口都是假装看不到也压根没用到,因此我也没打算用这个引脚。
Trig:触发控制,信号输入。就是从这个引脚输入10us触发信号,该引脚发出10us的高电平触发模块进行一次测距。
Echo:接收端,回响信号接收。该引脚可以在接收到回响信号后产生一个和距离成比例的方波,可以通过计时器进行计时,我认为也可以用输入捕获进行计时。
**2.介绍(注意事项)
HY-SRF05 超声波测距模块可提供2cm-450cm 的非接触式距离感测功能,测距精度可达高到3mm,其实这个精度也取决于单片机的晶振。
注意:
1、此模块不宜带电连接,若要带电连接,则先让模块的GND 端先连接,否则会影响
模块的正常工作。
2、测距时,被测物体的面积不少于0.5 平方米且平面尽量要求平整,否则影响测量的
结果
**3.原理&时序图
(1)采用IO 口TRIG 触发测距,给至少10us 的高电平信号;
(2)模块自动发送8 个40khz 的方波,自动检测是否有信号返回;
(3)有信号返回,通过IO 口ECHO 输出一个高电平,高电平持续的时间就是超声
波从发射到返回的时间。(4)计算公式
公式:uS/58=厘米或者uS/148=英寸;
或是:距离高电平时间*声速(340m/s)/2;
建议测量周期为60ms以上,以防止发射信号对回响的影响。
可参考这篇文章:
http://t.csdn.cn/acp4L
以上时序图表明你只需要提供-一个10uS以上脉冲触发信号,该模块内部将发出8个40kHz周期电平并检测回波。
一旦检测到有回波信号则输出回响信号。
回响信号的脉冲宽度与所测的距离成正比。
由此通过发射信号到收到的回响信号时间间隔可以计算得到距离。
2.代码配置
**1.思路
通过控制寄存器,控制一个IO口和Trig相连接,发出一个10us的触发信号。在用一个上拉输入的IO口和Echo相连接,当模块Echo引脚返回高电平时,用计时器开始计数,Echo低电平时计时器停止计时,通过计算收到的波长时间,根据公式us/29.4即可得出对应的距离(单位为厘米)。
**2.代码
主要是调用定时器实现计时,将Echo返回的时间(us)通过公式计算得到距离;
实际写代码的过程中发现,该模块的精度并不高,距离小于1cm后值会跳变,大于20cm也会跳变,注意设置合适的阈值。
#include <STC89C5xRC.H>
#include "lcd1602.h"
unsigned int xcm,us,s;
/***************************************************/定时器/计数/封装
void timer0_init() //这里定时器用作计数器初始化
{
TMOD&=0XF0;
TMOD|=0X01;
TF0=0;
TR0=0; //给0,先禁止计数
// TH0=0XFC; //这是1ms
// TL0=0X18;
// TL0 = 0x9C; //设置定时初始值
// TH0 = 0xFF; //设置定时初始值
TH0=0;
TL0=0;
// ET0=1; //计数器不需要中断
// EA=1;
// PT0=0;
}
void timer0_setnum(unsigned int star)//设置时间起始值
{
TH0=star/256;
TL0=star%256;
}
unsigned int timer0_getnum() //获取当前时间
{
unsigned int timenow;
timenow=TH0*256+TL0;
//timenow=(TH0<<8)|TL0;//两种一样的
return timenow;
}
void timer0_run(unsigned char runFlag) //开启计数
{
TR0=runFlag;
}
/***************************************************/延时函数
void Delay15us() //@12.000MHz
{
unsigned char i;
i = 5;
while (--i);
}
/***************************************************/csbo封装,超声波
sbit Trig=P1^2;
sbit Echo=P1^1;
sbit OUT=P1^0;
void csbo_init() //初始化
{
timer0_init();
timer0_setnum(0);
Trig=0;
}
void csbo_send()//发送触发信号,开始测量
{
Trig=0;
Trig=1;
Delay15us();
Trig=0;
}
void csbo_receive()//接收测量时间
{
timer0_setnum(0);
while(Echo==0);
timer0_run(1);
while(Echo==1);
timer0_run(0);
us=timer0_getnum();
}
void csbo_work(unsigned char flag)
{
if(flag)
{
csbo_send();
csbo_receive();
//s=us/1000000;
//xcm=(s*340)/200;
xcm=us*0.017;
}
}
/***************************************************/主函数
void main()
{
LCD_Init();
csbo_init();
while(1)
{
csbo_work(1);
Delay15us() ;
LCD_ShowNum(1,1,xcm,5);
}
}
/*********************阿布君***********************/
/****************编码不易,谢谢关注*****************/
/****************QQ:2062808868********************/