国信长天单片机竞赛训练之原理图讲解及常用外设原理(遗失的章节-零)
蓝桥杯单片机电子类基本模块使用,以鄙人之笔抛砖引玉~简单的说,锁存器就是一个大门,锁存器打开, 这时控制P0的电位变化,信号则被送进去,锁存器关闭,外界P0无论信号怎么变,一般来说只要该外设锁存器未打开,都不会对此外产生影响。(为什么说一般,嘿嘿,后面有个...
目录
1.2 数码管(板载共集成8位共阳级数码管),数码管分段选和位选
基本外设
1.1锁存器,led灯,蜂鸣器,继电器及注意事项
单片机竞赛(蓝桥杯电子类)用的开发板的芯片是stc15f2k60s2,我第一次拿到这板子的时候,发现和其他我之前买的最小开发板最大的不同是它有锁存器,共有4个(Y4,Y5,Y6,Y7),锁存器(单片机板子上的锁存器由若干个D触发器组成)的作用是保持之前输入信号的状态!
-- Y4锁存器控制led灯,P0输入低 电平点亮,输入高电平熄灭
-- 蜂鸣器低电平响,高电由于ULN2003反相器 的 存 在,输入为低电平灭,高电平响。
--- 继电器,输入低电平不工作,高电平工作
由上可知 Y5锁存器控制蜂鸣器 继电器等 ,ULN2003相当于一个反相器。
而输入信号Y4C.Y5C,Y6C,Y7C则由以下或非门(实际就相当于非门)输出得到,而Y4,Y5,Y6,Y7则由74138译码器得到
P2的高三位控制,所以
Y4:P2=P2&0x1f|0x80;
Y5:P2=P2&0x1f|0xa0;
Y6:P2=P2&0x1f|0xc0;
Y7:P2=P2&0x1f|0xe0;
为什么这样写:以Y4为例,P2&0x1f把高三位置为0,再位或0x80,则把高三位赋值为100,
对于的译码器即为Y4',次数Y4'输出为0有效,而自始至终其他位都未变,
实际上这个单片机只用了板载的外设,P2除高三位外,其他位都没用,大家直接赋值0x80也没问题。
简单的说,锁存器就是一个大门,锁存器打开, 这时控制P0的电位变化,信号则被送进去,锁存器关闭,外界P0无论信号怎么变,一般来说只要该外设锁存器未打开,都不会对此外产生影响。
注意:单片机电位变化只有高低两种电平,而单片机板子上的LED灯,蜂鸣器,数码管段选和位选都是P0口控制 ,这时候大家就知道锁存器的作用了,没错,P0总不可能同时精确的控制多个外设若是设计不加锁存器,则很容易出现控制不准确,而加了锁存器后,相应的外设就对应了多个锁存器,要控制那个外设先初始化锁存器的状态和P0口的状态,然后再给P0赋值,再开对应外设的锁存器,然后再关闭锁存器。
比如点个灯:
//比如点个灯
P0=0x00;//先开P0,0有效,即为全部点亮
P2=(P2&0x1f)|0x80;
P2=P2&0x0f;
/*
P2&0x1f把高三位置为0,再位或0x80,则把高三位赋值为100,
对于的译码器即为Y4',次数Y4'输出为0有效,而自始至终其他位都未变,
实际上单片机只用了板载的外设,P2除高三位外,其他位都没用,大家直接赋值0x80也没问题
*/
//注意:为什么要先初始化P0,如果要打开锁存器的话,P0的状态就直接输入进去了,可能会存
//在的控制不准确,这在后面多次操作数码管或是蜂鸣器更为明显
比如开机默认关闭led灯,数码管段位选
P0=0xff;P2=(P2&0x1f)|0x80;P2&=0x1f;//关led
buzzer=0;relay=0;P2=(P2&0x1f)|0xa0; P2&=0x1f;//关蜂鸣器和继电器
P0=0xff;P2=(P2&0x1f)|0xc0; P2&=0x1f;//关位选
P0=0xff;P2=(P2&0x1f)|0xe0;P2&=0x1f;//关段选
(可以看到我每次都是先对P0进行操作,再对锁存器进行操作,这个一般用于初始化关闭蜂鸣器,led,数码管)
数码管
1.2 数码管(板载共集成8位共阳级数码管)数码管分段选和位选
段选:控制每一位数码管亮的二级管的个数,比如上面的a,b,c,d等等
比如:
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff(低电平点亮)
0 1 2 3 4 5 6 7 8 9 全灭
位选:控制8位数码管中那几位点亮,比如com1-com8(高电平点亮)
对于静态数码管,直接选段,选位再点亮就行
对于动态数码管:利用人眼的视觉暂留,配合定时器中断,每2ms刷新一次,就能达到稳定的效果
emmmm,这里牵扯到定时器,干脆直接合着一起写了:
定时器:
对于比赛而已,算定时的时间是不可能手算的:我们有stcisp下载工具,一般定时选择12T
从这copy过去就可,使用的时候使能EA(全局总中断)和对于的定时器
注意:定时器的中断号:定时器0为1,而定时器1为3:,中断函数名可以任意写
使用:比如以下动态数码管
#include "stc15f2k60s2.h"
u8 code smg_index[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0x7f,0xbf};
u8 DT[]={10,10,10,10,10,10,10,10};
u16 s=0;
u8 i=0;
void sys_init(){
P0=0xff;
P2=P2&0x1f|0x80;
P2&=0x1f;
P0=0;
P2=P2&0x1f|0xe0;
P2&=0x1f;
P2=P2&0x1f|0xc0;
P0=0;
P2&=0x1f;
P2=P2&0x1f|0xa0;
P04=0;
P06=0;
P2&=0x1f;
}
void Timer0Init(void) //1000微秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xCD; //设置定时初值
TH0 = 0xD4; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA=1;
ET0=1;
}
void smg_show(unsigned char du,unsigned char wei){// DT[S] s
P0=1<<wei;
P2=(P2&0x1f)|0xc0;//位选
P2=P2&0x1f;
P0=smg_index[du]; //段选
P2=(P2&0x1f)|0xe0;
P2=P2&0x1f;
}
void main(){
while(1){
sys_init(); //系统初始化关蜂鸣器等函数
Timer0Init();//初始化定时器
//使用动态数码管
DT[0]=i++;//数码管第一位显示1
if(i>8)i=0;
}
}
void Time0() interrupt 1{
smg_show(DT[s],s);//只有0-7八位数码管变化
if(++s==8){
s=0;
}
}
每当DT[]数组里的数变化后,每次定时器中断则显示对于位修改的数,不修改,段选为0xff不显示。
1.3 矩阵键盘和独立键盘
独立键盘:跳线帽接 2 3
使用:
u8 key_val=0;
P3=0xf0&P3|0xf0
//判断
if(!P30)key_val=7;
else if(!P31)key_val=6;
else if(!P32)key_val=5;
else if(!P33)key_val=4;
矩阵键盘:跳线帽 接 1 2
以状态机的方式实现:
u8 key_scan(){
static key_state=0,key_return=0; //每次返回值和状态进来不会重置
u8 key_press;
switch(key_state)
{
case 0: //未按按键时的状态
key_return=0; //返回值置零
P3=0x0f; P42=0; P44=0;
key_press = P3&key_low;
if(key_press != 0x0f)
key_state=1;
break;
case 1: //按键按下去后第一次进scan_key的状态,每按一次按键只会进来case 1一次
P3=0x0f; P42=0; P44=0;
key_press = P3&key_low;//key_low为0x0f 同理key_high为0xf0
if(key_press != 0x0f)
{
if(key_press==0x0e) key_return=7;
if(key_press==0x0d) key_return=6;
if(key_press==0x0b) key_return=5;
if(key_press==0x07) key_return=4;
P3=0xf0; P42=1; P44=1;
key_press = P3&key_high;
if(key_press==0xe0) key_return+=12;
if(key_press==0xd0) key_return+=8;
if(P42==0) key_return+=4;
key_state = 2;
}
break;
case 2: //按键一直按着的状态
P3=0x0f; P42=0; P44=0;
key_press=P3&key_low;
if(key_press == 0x0f) //放开按键后key_state = 0回到case 0
{
key_state = 0;
}
default:
break;
}
return key_return;
}
C语言不好的注意:函数内有两个static 修饰的静态局部变量
静态局部变量,在函数里定义,就只能在这个函数里使用,同一个文档中的其他函数也是用不了的。由于被static修饰的变量总是存在内存的静态区。所以即使这个函数运行结束,这个静态变量的值不会被销毁(即不会被重新定义和赋值为0,这一点很多人刚开始忘记了),函数下次使用时仍能使用。
状态机的基本原理:分为初始的状态(case0) 按下的状态(case1)已经按下的状态(case 2),以轮询的方式查按键的状态(正如代码所示),其中key_return 可以用来记录按键是否被按着未松开。
细节可以参考:
蓝桥杯单片机之按键扫描(状态机)_昊月光华的博客-CSDN博客_基于状态机的按键扫描程序
1.4 数码管消影:
这是在之前的段选和位选上进一步消除影子,为达极致的显示:
参考理论:蓝桥杯单片机开发板的数码管的消影_昊月光华的博客-CSDN博客
数码管消影后的动态扫描算法,与上文写的smg_play相比
void smg_play(u8 du,u8 we){
P0=0xff;
lock(7); //消影
P0=0;
P0=1<<we;
lock(6);
P0=0xff;
P0=SMGINDEX[du];
lock(7);
}
1.5 按键的长按和短按(嵌套if实现)
/*
功能:判断代码长按短按
长按执行临界操作或者是持续性操作
短按则执行单一操作因为不存在持续性
*/
void los(u8 keys){
//有按键按下
if(key_v==keys&&!longf){
longf=1;
longt=0;
}
if(longf==1&&(longt++>1000)){
longf=2;
DT[1]=9;执行长按达到临界值功能功能
}
if(longf==2&&(longt++%1000==0)){
q++;
DT[2]=q/10%10;
DT[3]=q%10;
}
if((longf==1||longf==2)&&!key_v){//松开按键
if(longt<=1000){//执行短按
DT[1]=7;
}
longf=0;
}
}
效果:短按显示7 ,长按显示9(临界操作),后每隔1s显示q++的操作
1.6 按键的长按和短按(状态机实现)
/*
功能:判断代码长按短按
长按执行临界操作或者是持续性操作
短按则执行单一操作因为不存在持续性
*/
void los(u8 keys){
switch(longf){
case 0:
longt=0;
DT[2]=0;
DT[3]=0;
if(key_v==keys)longf=1;
break;
case 1:
if(key_v==keys){
if(longt++>1000){//达到长按临界值执行长按的瞬间性操作
DT[1]=9;
longf=2;
}
}
else {
if(longt<=1000){ //执行短按
DT[1]=7;
}
longf=0;
}
break;
case 2:
if(key_v==keys){
if(longt++%1000==0)q++;//0-65535
DT[2]=q/10%10;
DT[3]=q%10;
if(longt==60000)longt=0;
//执行长按的连续性操作
}
else
longf=0;
break;
}
}
效果:短按显示7 ,长按显示9(临界操作),后每隔1s显示q++的操作,复位后q的显示消失。
常用外设
2.1 DS18B20温度传感器
该温度传感器是基于one wire 单总线协议
参考单总线协议(1-wire)的基本原理 - 知乎 (zhihu.com)
典型的单总线命令序列如下:
- 第一步:初始化
- 第二步:ROM命令(跟随需要交换的数据)
- 第三步:功能命令(跟随需要交换的数据)
初始化:
时序见图 2.25-2 主机总线 t0 时刻发送一复位脉冲(最短为 480us 的低电平信号) 接着在 t1 时刻释放总线并进入接收状态 DSl820 在检测到总线的上升沿之后 等待 15-60us 接着 DS1820 在 t2 时刻发出存在脉冲(低电平 持续 60-240 us) 如图中虚线所示以下子程序在 MCS51 仿真机上通过 其晶振为 12M. 初始化子程序
sbit DQ=P1^4;
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;//这句不加也可以
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);//拉低延长400-960us
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
void Delay_OneWire(unsigned int t)
{
/*传统8051单片机是12T,而15单片机是1T,理论上应该扩大延时为12倍,实际上8-12倍都可以*/
unsigned char i;
while (t--)
{
for (i = 0; i < 8; i++)
;
}
}
ROM命令
写时间间隙:(发送命令)
当主机总线to时刻从高拉至低电平时 就产生写时间隙见图2.25.3 图 2.25.4 从 to 时刻开始 15us 之内应将所需写的位送到总线DSl820 在t后15-60us 间对总线采样,若低电平写入的位是0,见图 2.25.3;若高电平写入的位是1见图2.25.4。连续写2位间的间隙应大于1us 。
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for (i = 0; i < 8; i++)
{
DQ = 0;
DQ = dat & 0x01;//将dat的最低位数据送总线
Delay_OneWire(5);
DQ = 1;
dat >>= 1;//dat右移一位得次低位
}
Delay_OneWire(5);
}
读时间间隙:(读取数据)
首先将数据线拉低然后延长4us,再将数据线拉高释放总线准备读取数据,延时10us,读数据线状态得到一个状态位并进行数据处理,延时45us,循环重复,直到读完一个字节(八位数据)
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for (i = 0; i < 8; i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;//采样信号,释放总线准备读取数据
if (DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
功能命令
读取温度:
DS18B20是存取16位数据,分为高八位和低八位,高八位的高四位为符号位,低八位的低四位是小数位,其余为为整数位,另外ds18b20的资料代码中读取采样的数据是从低位开始读取。
获取温度函数编写
首先初始化设备,向单总线发送0xcc(ROM指令)指令,跳过ROM搜索,发送0x44(RAM命令如上图)指令开启温度转换。继续初始化设备,继续跳过ROM搜索,发送0xbe(RAM命令)读取命令。把读到的数据低八位赋值变量low,高八位赋值high。然后把数值转换成小数返回。
float rd_temperature(void)
{
unsigned char low,high;/*用来存暂存器的值*/
unsigned int temp;/*取整数*/
float result;
init_ds18b20();//搜寻总线上有没有DS18B20设备
Write_DS18B20(0xcc);/*跳过rom搜索*/
Write_DS18B20(0x44);/*开启温度转换*/
init_ds18b20();//搜寻总线上有没有DS18B20设备
Write_DS18B20(0xcc);/*跳过rom搜索*/
Write_DS18B20(0xbe);/*告诉总线准备读取暂存器*/
//DS18B20 16位 高八位的低四位和低八位的高四位为整数
low=Read_DS18B20();//低八位
high=Read_DS18B20();//高八位
temp =(high&0x0f);
temp<<=8;
temp|=low;
result =temp*0.0625;
return result;
}
2.2 串口
1:首先初始化串口
void UartInit(void) //9600bps@11.0592MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x40; //定时器1时钟为Fosc,即1T
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设定定时器1为16位自动重装方式
TL1 = 0xE0; //设定定时初值
TH1 = 0xFE; //设定定时初值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
//ES=1; // ES EA 为中断控制位,若开启串口中断 若开启串口中断则还要写中断函数
//EA=1;
}
2:写串口接受和发送函数
void send_char(unsigned char chr)//发送一个字节的数据
{
SBUF = chr;
while (!TI)
; //硬件发送以后自动置1
TI = 0;
}
void send_string(unsigned char *str)//发送一个字符串
{
while (*str)
{
send_char(*str);
str++;
}
}
void rear_char(unsigned char *p)//接受字符串
{
char *temp;//字符临时变量
if(RI){
RI=0;
*temp=SBUF;
send_char(*temp);
}
SBUF为发送和接受数据的缓冲区
SBUF=xxx(即为SBUF写在左边为发送数据)而xxx=SBUF(SBUF写在右边为接受数据)TI为发送数据的标志位,正在发送数据 TI硬件置0,发送完后硬件置1,为了连续发送故再软件置0。
3:使用串口
通过调用串口的接受和发送函数来使用串口。
2.3 超声波:
工作原理:
(简单的说,就是超声波配合计数器,发送方波后打开计时,然后计算时间乘以速度(340m/s)/2得距离)
需要注意的是想要计算的路程足够远,你计数器计时的时间得足够长,所有得用12T的模式,周期为1us,1T的周期为1/12us,最长时间1us*65535,最长距离
340*65535(ms)/1000000*100/2=0.017*65535(cm)(这也是计算公式)
sbit Tx=P1^0;
sbit Rx=P1^1;
void Timer0Init(void) //1微秒@11.0592MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0x0F; //设置为计数器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x00; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 0; //定时器关闭计时
}
void csb_start(void)
{
u8 i=8;
EA=0;//关闭总中断,防止其他中断占用cpu导致并未连续发送方波而计算不准确
while(i--)
{
Tx=1;
Delay12us();
Tx=0;
Delay12us();
}
EA=1;
}
unsigned int get_dis(void)
{
u16 dis;
Rx=1;
csb_start();
TR0=1;//开启计数器
while((Rx==1 && TF0==0));//数据未溢出或是未接受到方波时继续等待
TR0=0;//关闭计数器
if(TF0==1)//判断数据溢出标准位
{
dis=999;TF0=0;
TH0=0;TL0=0;
}
else//正常接受到方波
{
dis=(TH0<<8)|TL0;//高八位左移八位再或上低八位再赋给16位数据dis
TH0=0;TL0=0;
dis=(dis*0.17)/10;
if(dis <= 2 || dis >= 400)
dis=999;
}
return dis;
}
2.4 iic总线
2.4.1iic总线概述:
每个具有IIC接口的设备都有一个唯一的地址,也叫做设备地址
2.4.2 总线数据传输规范
IIC总线协议规定,每传送一个字节数据后,都要有一个应答信号,以确定数据传送是否被对方收到。应答信号由接收方在数据开始后的第9个时钟周期发送,在SCL为高电平期间,接收方将SDA拉为低电平产生应答,用来结束一个字节的传输。也就是说,一帧完整的数据共有9位。 注意:当主机接收数据(也就是在读数据状态)时,它收到最后一个字节后,必须向从机发出一个结束传送的信号。这个信号是通过对从机的“非应答信号”来实现的,在SCL为高电平期间,SDA为高电平,即从机释放SDA线,允许主机产生一个停止信号。
写入时序:
2.4.3 总线驱动程序设计
•在没有硬件IIC外设的微处理器中,需要根据总线时序设计IIC接口的驱动程序。包括:起始信号、停止信号、产生应答、等待应答、发送数据和接收数据6个函数。
/*
程序说明: IIC总线驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台 8051,12MHz
日 期: 2011-8-9
*/
#include "reg52.h"
#include "intrins.h"
#define DELAY_TIME 5
#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1
//总线引脚定义
sbit SDA = P2^1; /* 数据线 */
sbit SCL = P2^0; /* 时钟线 */
void IIC_Delay(unsigned char i)
{
do{_nop_();}
while(i--);
}
//总线启动条件
void IIC_Start(void)
{
SDA = 1;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 0;
}
//总线停止条件
void IIC_Stop(void)
{
SDA = 0;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 1;
IIC_Delay(DELAY_TIME);
}
//发送应答
void IIC_SendAck(bit ackbit)
{
SCL = 0;
SDA = ackbit; // 0:应答,1:非应答
IIC_Delay(DELAY_TIME);
SCL = 1;
IIC_Delay(DELAY_TIME);
SCL = 0;
SDA = 1;
IIC_Delay(DELAY_TIME);
}
//等待应答
bit IIC_WaitAck(void)
{
bit ackbit;
SCL = 1;
IIC_Delay(DELAY_TIME);
ackbit = SDA;
SCL = 0;
IIC_Delay(DELAY_TIME);
return ackbit;
}
//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++)
{
SCL = 0;
IIC_Delay(DELAY_TIME);
if(byt & 0x80) SDA = 1;
else SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 1;
byt <<= 1;
IIC_Delay(DELAY_TIME);
}
SCL = 0;
}
//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
unsigned char i, da;
for(i=0; i<8; i++)
{
SCL = 1;
IIC_Delay(DELAY_TIME);
da <<= 1;
if(SDA) da |= 1;
SCL = 0;
IIC_Delay(DELAY_TIME);
}
return da;
}
2.4.4 总线驱动程序的应用
2.4.4.1:
EEPROM(AT24C02)
AT24C02的EEPROM芯片地址的固定部分为1010,片选端A2、A1、A0得到固定的3位编码.形成的7位编码即为该器件的地址码。
//EEPROM 写入
void AT24C02_WB(u8 ward,D)
{
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(ward);
IIC_WaitAck();
IIC_SendByte(D);
IIC_WaitAck();
IIC_Stop();
}
//EEPROM 读取
u8 AT24C02_RB(u8 ward)
{
u8 D;
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(ward);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0xa1);
IIC_WaitAck();
D=IIC_RecByte();
IIC_Stop();
return D;
}
2.4.4.2:
数模转换与模数转换(PCF8591)DAC与ADC
PCF8591是具有IIC接口的8位A/D和D/A转换芯片,具有4路模拟输入、一路DAC输出和一个IIC总线接口。
PCF8591的设备地址包括固定部分和可编程部分。可编程部分需要根据硬件引脚A0、A1和A2来设置。设备地址的最后一位用于设置数据传输的方向,即读/写位。
PCF8591的设备的读操作地址为:0x91;而写操作地址则为:0x90。
控制寄存器:
光敏传感器接到AIN1,通道1;控制寄存器应写入:0x01。
电位器Rb2接到AIN3,通道3;控制寄存器应写入:0x03。
void write_DAC(u8 dat)
{
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(0x40);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
} //DAC写入
//adc读取
u8 ReadADC(u8 ain)
{
u8 Data;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(ain);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
Data=IIC_RecByte();
IIC_Stop();
return Data;
}
使用例如读取光敏传感器和RB2电位器的采样值,存取EPPROM。
AT24C02_RB(1);
AT24C02_WB(1,adc_val)//adc_val为要写入的值
ReadADC(0x03);//0x03 RB2电位器
ReadADC(0x01);//0x01 光敏传感
2.5 NE555频率测量
说明
这个NE555频率测量需要结合定时器0,并且设置为计数模式,按键,数码管等其他外设则用定时器1就行,NE555在数电中被叫做三五定时器,可用于构建单稳态电路,多谐振荡器和斯密特触发电路,在蓝桥杯(stc15f2k60s2)的开发板中,是通过调节RB3电位器的电阻值以调节脉冲频率。
使用
用竞赛板子上的P34与SIGNAL短接,用跳线帽或杜邦线(若题目不涉及超声波和红外则可借用超声波下方的跳线帽)。
void Timer0Init(void)
{
AUXR |= 0x80;
TMOD |= 0x05;
TL0 = 0x00; //设置计数初值
TH0 = 0x00; //设置计数初值
TF0 = 0;
TR0 = 0;
ET0 = 0;
}//初始化定时器0
void Timer1Init(void) //1000微秒@12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x20; //设置定时初始值
TH1 = 0xD1; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
EA=1;
ET1=1;
}//初始化定时器1
//在main.c中
u16 fr=0;//频率
u16 frtime=0;
TR0=1;//开始计数
while(1){
//读出1s的脉冲个数也就是频率
if(frtime==500){
frtime=0;
TR0=0;//结束计数
fr=TH0<<8|TL0;
TH0=0;
TL0=0;//清空高八位和低八位计数
fr*=2;//fr*2也就是1s中计数的脉冲个数
TR0=1;//开始下一次计数
}
//todo
}
void time1() interrupt 3{//定时器1中断函数 1ms产生一次中断
frtime++;
//to do
}
2.6 DS1302实时时钟
说明
其时序图文档中都有,而且比赛提供的资料中提供了底层代码,我们只需要调用就行了。
时序图:
读写命令
//DS1302驱动文件提供的函数
#ifndef __DS1302_H
#define __DS1302_H
void Write_Ds1302(unsigned char temp);
void Write_Ds1302_Byte( unsigned char address,unsigned char dat );
unsigned char Read_Ds1302_Byte( unsigned char address );
#endif
使用
由于DS1302读和写的数据都是BCD码,一个字节高四位表示十位,低四位的数值即为剩余的数值。比如 0x15 BCD码为 0001 0101 转换为显示的十进制为1*10+5。
u8 DatToBcd(u8 dat)//数据转BCD码
{
u8 dat1,dat2;
dat1 = dat / 10;
dat2 = dat % 10;
dat2 = dat2 + dat1 * 16;
return dat2;
}
u8 BcdToDat(u8 dat)//BCD码转数据
{
u8 dat1,dat2;
dat1 = (dat >>4)*10;
dat2 = (dat&0x0f) % 16;
dat2 = dat2 + dat1;
return dat2;
}
初始化写时间函数(如 写 年 周 月 日 时 分 秒)
void DS_INIT(){
Write_Ds1302_Byte(0x8e,0);//清除写保护
Write_Ds1302_Byte(0x80,DatToBcd(30));//30秒
Write_Ds1302_Byte(0x82,DatToBcd(15));//15分
Write_Ds1302_Byte(0x84,DatToBcd(15));//15时
Write_Ds1302_Byte(0x86,DatToBcd(1));//日
Write_Ds1302_Byte(0x88,DatToBcd(15));//15月
Write_Ds1302_Byte(0x8a,DatToBcd(1));//周一
Write_Ds1302_Byte(0x8c,DatToBcd(15));//15年
}
读取时间函数(比如 年 周 月 日 时 分 秒)
void Read_Time(){
MyDst[0]=BcdToDat(Read_Ds1302_Byte(0x8d));//年
MyDst[1]=BcdToDat(Read_Ds1302_Byte(0x8b));//周
MyDst[2]=BcdToDat(Read_Ds1302_Byte(0x89));//月
MyDst[3]=BcdToDat(Read_Ds1302_Byte(0x87));//日
MyDst[4]=BcdToDat(Read_Ds1302_Byte(0x85));//时
MyDst[5]=BcdToDat(Read_Ds1302_Byte(0x83));//分
MyDst[6]=BcdToDat(Read_Ds1302_Byte(0x81));//秒
}
每隔1s定时读取时间就实现了实时时钟。
2.7 PWM的控制
参考应用
基于单片机的PWM输出对Led的常见应用_昊月光华的博客-CSDN博客
找个隐蔽的地方写:愿君能有所收获,正所谓 “何以与君识”,“无言码千行”。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)