1、作品功能
考虑到养一盆花需要花费一些精力,所以设计此次作品。其主要功能有通过检验土壤湿度值来实现自动浇花,当然在有水源的情况下。自动浇花的过程会伴随美妙的音乐(鬼畜的音乐也行,只要你想得到的,没有软件实现不了的,嘿嘿)响起,音乐结束,浇花过程停止,其上还可以通过按键选择显示屏显示时间,温度,同时也可通过按键操作来进行调整时间。
2、器件选择及实施方案
下面给出一张结构图
(1)、通过ds1302时钟芯片使万年历得以实现
(2)、通过DS18b20温度传感器检测外界温度
(3)、通过L298N来控制电机也即水泵的正反转,通过使用单片机内部定时器调节占空比并产生PWM波来达到控制电机转速目的,使其拥有了面对不同大小植物的对水的需求量的调节能力
讲一下L298N怎么接线,以及要注意的一些问题。(1)、IN1和IN2,IN3和IN4是两两配对的输入口,可以自定义单片机IO口,他们主要读取的就是这两个脚的电平高低,不过首先把EN接入单片机任意一个IO口,EN有两个,你如果用的是IN1、IN2就用ENA通道,同理另外两个用ENB,再说OUT也有两个(接你水泵,电机的,正反一样),与IN一样有4个,out1、out2是当IN1、IN2、ENA接通时选择这个,out3、out4与之同理(如果你不使用PWM其实EN就不需要接在单片机上,并盖好他们的跳帽,也就是短接(那个黄黄,或者黑黑的东西)),
注意:
L298N有两种电源接法12V的,5V的,自己看着接。
L298N供电的5V如果是用另外电源供电的话,(即不是和单片机的电源共用),那么需要将单片机的GND和模块上的GND连接在一起,(第三根线,看下图)只有这样单片机上过来的逻辑信号才有个参考0点。此点非常重要。我一直还以为是电压不够,也以为是水泵两边与导线接触点没焊好,后来才发现是这个问题。
好,再讲讲PWM怎么实现的,以及占空比怎么设置,高中学过方波电压图像,电压有正有负,利用功率相等,应该很容易能计算出平均电压的值吧!,举个栗子,我们对IN1,IN2赋值一个1,一个0,那么电压就是IN1所接电压大小,如果我们让IN1在一毫秒内有半毫秒是0,那么IN1平均电压为之前的一半了,此时也引出占空比为50%,一半是空的嘛!我们在升级一下,那个所谓1ms我们用定时器中断来实现,1ms的频率被称为1000HZ,频率太大电机会抖,太小电机不会转起来,我们都学过滑动变阻器来调节电压大小,但其通过硬件实现,我们引入占空比就是类比于一个软件式或者说一个虚拟的滑动变阻器来调节电机电压大小
如设置一个定时时间为0.01ms的定时器,中断一百次就是1ms,在中断子程序内,可设置一个变量如time,在中断子程序内,有三条重要的语句:1、当time>=100时,time清零(此语句保证频率为1000HZ),2、当time>n时(n应该在0-100之间变化开),让单片相应的I/O口输出高电平,当time<n时,让单片相应的I/O口输出低电平,此时占空比就为%n。如果要实现调速,你就需要另一个变量count令其初值50(为了使其开始电压够大,方便后面的加减)此时中断程序那抽象的三个语句变成了(1)当time<count,IN1为高电平,电压就又会变化(2)当time>=100时,time清零(3)进入中断时IN2 = 0,且time自增,看例子吧,还有调速,就是随便哪个按键按下使count加10或者减10,(至于蓝牙来控制可能我有难度,因为蓝牙基于串口通信,其中断优先级最低,你可能需要去设置IP地址重新定义一个执行优先级,很麻烦,不如用红外控制,红外所采取中断为外部中断0吧好像是。)这样来滑动这根虚拟的滑片,但要注意当count>100时要令count清零
void tim0() interrupt 1 { TR0=0; TH0=0xff; TL0=0x9c; TR0=1; IN3=0; time_PWM++; if(time_PWM<count) { IN4=1; } else IN4=0; if(time_PWM>=100) { time_PWM=0; } }
(4)YL69、XPT2046(ADDA芯片,采用SPI三线通信协议)YL69是湿度传感器,通过两边长得像脚一样的东西,通电后插入土壤中两脚会有一定的电压值,两脚不接东西,就是在空气中的时候
其上的模拟值为4095,如果想获得湿度与电压的大致关系,可以画一个表格,描点,大致上是一条直线,用回归直线方程可以求出相关系数
1电源:3.3V~5V
获取湿度信息的方式(2种可同时使用):
从传感器的D0引脚:土壤湿度大于某个阈值,则D0输出0,否则输出1。(多用于湿度阀值控制开关,这个引脚适合做湿度开关,通过与XPT2046的模拟通道口——AIN3连接即可,AO与你单片机的)
从传感器的A0引脚:获取到模拟量,更加精确。土壤湿度越大,获取的模拟量值越大。(多用于显示实时湿度值,也是接AIN3口,此时的D0随便接一个单片机的IO口)
附一张图:
上面是XPT2046的主要使用方式,当初纠结了很久,A2、A1、A0是用来选择工作通道是收取哪个工作通道的信息,如其为001时对应的是XP而XP这要看你开发板原理图对应哪个通道口,由于我用的是外部输入的模拟量所以我开发板我接了AIN3,而AIN3并非一个真实存在的(AIN2,AIN1一样都不一定有IO口的),而我的板子恰好把AIN3与AUX定义在一起,所以我对AUX的IO进行操作等价于对AIN3操作,其余类似,再看下图,就可以写出一个完整的ad模拟量转换数据量的程序,不过还是要先看懂其时序图才能写吧,要不然肯定是错的,那在看下图的时序图,很显然数据是12位一输入,通过DCLK一升一降来实现数据一位一位的传导
(5)蓝牙,中断,串口通信
终于可以说蓝牙了,好多人学单片机时用的红外,我没用,因为我觉得蓝牙绝对是高级的存在,一般人肯定随身携带手机啊,而且也没有专门手机软件来充当红外控制器的
蓝牙是基于串口通信,现在说下我的痛苦经验吧,调节AT指令必须借个串口在一个串口助手内打开,然后修改串口助手的波特率为38400,这样才能与蓝牙进行交流,不然,AT指令发送没反应。这是HC06蓝牙的方式,重复一遍hc06只有4个脚,HC05有六个脚并且其板子上还有个黑色按钮,用来进入AT模式,进入AT模式后,指示灯会慢闪,平常状态也就是可连接状态快闪,一秒末闪两次那种,其进入AT指令的方法是把KEY这个引脚用杜邦线接到单片机任意一个有高电平的IO口,拔下串口,再按下黑色按钮,再接上串口(也就是让蓝牙断电时按黑色按钮),之后再介绍几个基本的AT指令,每次发送AT指令时要加上回车键再点击发送,回车键在文本中表示为\r\n,不用写这文本表示的,按回车就行。如果这样都不能发送AT指令,那么就是蓝牙本身硬件的问题,我当初被那个破HC06坑得好惨,浪费大把时间,其实就是坏了,换个就好。注意啊,修改了蓝牙的波特率,你串口通信和蓝牙交流时要修改与其相同的波特率,如果没注意,用罗敬忠大佬的话来说,你就是个比我还铁的铁憨憨。
HC-05 可以主从切换模式,但是HC-06虽然可以做主机也可以做从机,但是不能切换模式
模块进入AT响应状态:波特率:9600 数据位 :8位 停止位:1位 无校验位//这是对于11.095M晶振来说误差最小,而现在一般是12M晶振,应把蓝牙波特率修改为2400这样的误差才是最小值,否则误差太大,发送会是乱码,误差为0.16%
HC-05指令 |
HC-06指令 |
|
测试指令 |
发送:AT 响应:OK |
发送:AT 响应:OK |
模块复位 |
发送:AT+RESET 响应:OK |
|
获取版本号 |
发送:AT+VERSION? 响应:+VERSION:<Param>OK |
|
恢复默认状态 |
发送:AT+ORGL 响应:OK |
|
获取模块蓝牙地址 |
发送:AT+ADDR? 响应:+ADDR: <Param>OK |
|
设置/查询设备名称 |
发送:AT+NAME=<Param> 响应:OK 发送:AT+NAME? 响应:1、 +NAME:<Param> |
发送:AT+NAMEname 响应:OKname |
获取远程蓝牙设备名称 |
发送:AT+RNAME? <Param1> 响应:1、 +NAME:<Param2> |
|
设置模块角色 |
发送:AT+ROLE=<Param> 响应:OK |
发送:AT+ROLE=M(设置为主模式Master) 响应:OK+ROLE:M 发送:AT+ROLE=S(设置为主模式Slave) 响应:OK+ROLE:S |
查询模块角色 |
发送:AT+ ROLE? 响应:+ ROLE:<Param> 0——从角色( Slave) |
|
设置设备类 |
发送:AT+CLASS=<Param> 响应:OK |
|
查询设备类 |
发送:AT+ CLASS? 响应:1、 + CLASS:<Param> OK——成功 Param:设备类 |
|
设备查询访问码 |
发送:AT+IAC=<Param> 响应:1、 OK——成功 |
|
查询访问码 |
发送:AT+ IAC? 响应:+IAC: <Param>OK |
|
设置/查询-配对码 |
发送:AT+PSWD=<Param> 响应:OK 发送:AT+ PSWD? 响应:+ PSWD : <Param> OK Param:配对码 |
发送:AT+PINxxx 响应:OKsetpin |
设置/查询串口参数 |
发送:AT+UART=<Param>,<Param2>, 响应:OK 发送:AT+ UART? 响应:+ UART=<Param>,<Param2>,OK |
发送:AT+BAUD(1、2、3、4) 响应:OK |
设置/查询连接模式 |
发送:AT+CMODE=<Param> 响应:OK 发送:AT+ CMODE? 响应:+ CMODE:<Param>OK Param: |
|
设置/查询绑定蓝牙地址 |
||
设置/查询连接状态 |
||
从蓝牙配对列表中删除指定认证设备 |
发送:AT+PMSAD=<Param>(蓝牙地址) 响应:OK |
|
从蓝牙配对列表中删除所有认证设备 |
发送:AT+RMAAD 响应:OK |
|
获取蓝牙工作状态 |
发送:AT+STATE? 响应:+ STATE: <Param> OK |
|
查询蓝牙设备 |
发送:AT+INQ 响应:+INQ: <Param1>,<Param2> <>OK |
出厂默认状态:
①.设备类:0
②.查询码:0x009e8b33
③.模块工作角色:Slave Mode
④.连接模式:指定专用蓝牙设备连接模式
⑤.串口参数:波特率—38400bits/s;停止位:1位;校验位:无
⑥.配对码:“1234”
⑦.设备名称:“H-C-2010-06-01”
模块角色说明:
Slave(从角色)——被动连接;
Slave-Loop(回环角色)——被动连接,接收远程蓝牙主设备数据并将数据原样返回给远程蓝
牙主设备;
Master(主角色)——查询周围 SPP 蓝牙从设备,并主动发起连接,从而建立主、从蓝牙设
备间的透明数据传输通道。
说下中断吧:
学中断的时候,那些一个又一个的寄存器让我晕头转向,怎么去使用与记住这些寄存器的用法和名字是最关键的:
对于单片机来讲,中断是指CPU在处理某一时间A时,发生了另一事件B请求CPU立刻去处理(中断发生);CPU暂时停止当前的工作(中断响应),转而去处理事件B(中断服务),待CPU处理事件B完成后,再回到原来事件A被中断的地方继续处理事件A(中断返回)。
① 中断源 // 5个中断源都有一个中断入口地址,当某个中断源产生中断时,CPU响应中断便到相应的中断入口地址执行中断服务程序
② 中断的嵌套与优先级处理
③ 中断的响应过程
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
中断系统结构
- 外部中断请求源:INT0、INT1
外部中断0(INT0)由外部引脚P3.2引入,外部中断1(INT1)由外部引脚P3.3引入
- 内部中断请求源:T0、T1、串口中断
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
程序定义方式
- 定义中断函数的一般形式
void 函数名() interrupt 中断号 using 寄存器工作组
p.s.如果中断函数中调用了其他函数,则被调用函数所使用的寄存器组必须与中断函数相同。中断函数不能参数传递,没有返回值,不能直接被调用。
- 中断序号
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
中断优先级
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
中断系统寄存器
- TCON:低4位给外部中断请求源使用,高4位给内部中断请求源定时器T0 T1使用
外部请求源
- IT0:INT0触发方式控制位,可由软件进行置位和复位。IT0=0时,INT0为低电平触发方式。IT0=1时,INT0为负跳变触发方式。
- IE0:INT0中断请求标志位。当有外部的中断请求时,该位置1(这由硬件来完成),在CPU响应中断后,由硬件将IE0清0。
- IT1、IE1的用途和IT0、IE0相似。
内部请求源
- TF0:定时/计数器T0溢出中断标记,当T0产生溢出时,TF0置位。当CPU响应中断后,硬件将TF0复位
- TR0:T0的开闭控制位,TR0=1时定时计数器打开,TR0=0时定时计数器关闭
- TF1、TR1与TF0、TR0相似。
- SCON:低2位与串口中断相关
内部请求源
- TI、RI:串行口发送、接收中断
- IE:中断允许寄存器
- EA:中断总控制位。EA=1,CPU开放所有中断;EA=0,CPU禁止所有中断。
- ES:串行口中断控制位。ES=1,允许串行口中断;ES=0,屏蔽串行口中断。
- ET1:定时/计数器TI中断控制位。ET1=1,允许T1中断;ET1=0,禁止T1中断。
- EX1:外部中断1中断控制位。EX1=1,允许外部中断1中断;EX1=0,禁止外部中断1中断。
- ET0:定时/计数器T0中断控制位。ET0=1,允许T0中断;ET0=0,禁止T0中断。
- EX0:外部中断0中断控制位。EX0=1,允许外部中断0中断;EX0=0,禁止外部中断0断。
- IP:中断优先级寄存器
在该寄存器中,优先级分为1 0两级,对应的位置为1则为高优先级,位置为0则为低优先级。执行时先将高优先级的中断执行完后才会执行低优先级(同样高优先级情况下,按默认优先级排)。
- PS:串行口中断优先级控制位
- PT1:定时器1优先级控制位
- PX1:外部中断1优先级控制位
- PT0:定时器0优先级控制位
- PX0:外部中断0优先级控制位
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
补充:
外部中断的触发方式选择
- 电平触发方式(低电平触发)
CPU在每个机器周期采样到的外部中断输入线的电平。在中断服务程序返回之前,外部中断请求输入必须无效(即变为高电平),否则CPU返回主程序后会再次响应中断。
适于外中断以低电平输入且中断服务程序能清除外部中断请求(即外部中断输入电平又变为高电平)的情况。
- 跳沿触发方式
连续两次采样,一个机器周期采样到外部中断输入为高,下一个机器周期采样为低,则置"1"中断请求标志,直到CPU响应此中断时,该标志才清0。这样不会丢失中断,但输入的负脉冲宽度至少保持1个机器周期。
下面是中断嵌套的相关操作,我看写得挺好就抄来了
转自:http://blog.chinaunix.net/uid-23215128-id-2521289.html
在MCS-中断优先级中由中断优先级寄存器IP来高置的,IP中某位设为1,相应的中断就是高优先级,否则就是低优先级。
- |
- |
- |
PS |
PT1 |
PX1 |
PT0 |
PX0 |
IP优先级别寄存器各位介绍如下:
PS:串行口中断优先级控制位。PS=1设定串行口为高优先级中断;PS=0为低优先级中断。
PT1:T1中断优先级控制位。PT1=1设定定时器T1为高优先级中断;PT1=0为低优先级中断。
PX1:外部中断1优先级控制位。PX1=1设定定时器外部中断1为高优先级中断;PX1=0为低优先级中断。
PT0:T0中断优先级控制位。PT0=1设定定时器T0为高优先级中断;PT0=0为低优先级中断。
PX0:外部中断0优先级控制位。PX0=1设定定时器外部中断0为高优先级中断;PX0=0为低优先级中断。
例:设有如下要求,将T0、外中断1设为高优先级,其它为低优先级,求IP的值。
IP的首3位没用,可任意取值,设为000,后面根据要求写就可以了
- |
- |
- |
PS |
PT1 |
PX1 |
PT0 |
PX0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
0 |
因此,最终,IP的值就是06H。
例:在上例中,如果5个中断请求同时发生,求中断响应的次序。
响应次序为:定时器0->外中断1->外中断0->实时器1->串行中断。
说最基本的,老的51单片机(80C51系列)有5个中断源,2个优先级,可以实现二级中断服务嵌套。现在很多扩展的51单片机已经有4个优先级(或更多)和更多的中断源了。
在说到中断之前,我先来定义一下优先级,明白了什么是优先级,后面的阐述就容易明白了。实际上很多人都是混淆了优先级的含义,所以才觉得糊里糊涂。
中断的优先级有两个:查询优先级和执行优先级。
什么是查询优级呢?我们从datasheet或书上看到的默认(IP寄存器不做设置,上电复位后为00H)的优先级:
外部中断0 > 定时/计数器0 > 外部中断1 > 定时/计数器1 > 串行中断
或 int0,timer0,int1,timer1,serial port 或 INT0、T0、INT1、T1、UART
或 PX0>PT0>PX1>PT1>PS>......
其实都是查询优级。首先查询优先级是不可以更改和设置的。这是一个中断优先权排队的问题。是指多个中断源同时产生中断信号时,中断仲裁器选择对哪个中断源优先处理的顺序。而这与是否发生中断服务程序的嵌套毫不相干。当CPU查询各个中断标志位的时候,会依照上述5个查询优先级顺序依次查询,当数个中断同时请求的时候,会优先查询到高优查询先级的中断标志位,但并不代表高查询优先级的中断可以打断已经并且正在执行的低查询优先级的中断服务。
例如:当计数器0中断和外部中断1(按查询优先级,计数器0中断>外部中断1)同时到达时,会进入计时器0的中断服务函数;但是在外部中断1的中断服务函数正在服务的情况下,这时候任何中断都是打断不了它的,包括逻辑优先级比它高的外部中断0计数器0中断。
而中断的执行优先级就是你对IP寄存器的设置了。在2个优先级的情况下,某位为1,则相应的中断源为高优先级;为0,则为低优先级。
关于中断的优先级有三条原则:
1、CPU同时接收到几个中断时,首先响应优先级最高的中断请求;
2、正在进行的中断过程不能被新的同级或低行优优先级的中断请求所中断;
3、正在进行的低行优优先级中断服务,能被高行优优先级中断请求中断;
若:同一执行优先级中的中断申请不止一个时,则有一个中断优先权排队问题。同一执行优先级的中断优先权排队,由中断系统硬件确定的自然优先级形成,优先权自高到低的顺序即:
外部中断0>定时/计数0>外部中断1>定时/计数1>串行接口
例如:设置IP = 0x10,即设置串口中断为最高优先级,则串口中断可以打断任何其他的中断服务函数实现嵌套,且只有串口中断能打断其他中断的服务函数。若串口中断没有触发,则其他几个中断之间还是保持逻辑优先级,相互之间无法嵌套。
关于中断嵌套。可以这样说,当一个中断正在执行的时候,如果事先设置了中断优先级寄存器IP,那么当一个更高优先级的中断到来的时候会发生中断嵌套,如果没有设置则不会发生任何嵌套;如果有同一个优先级的中断触发,它并不是在“不断的申请”,而是将它相应的中断标志位置即IE寄存器的某位置位,当CPU执行完当前中断之后,按照查询优先级重新去查询各个中断标志位,进入相应中断。
要记住,没有设置IP时,单片机会按照查询优先级(或都说逻辑优先级)来排队进入服务。如果要想让某个中断优先响应, 则要设置IP,更改执行优先级(或者说物理优先级)。要注意的是,当设置了IP后,当低执行优先级中断在运行时,如果有高执行优先级的中断产生,则会嵌套调用进入高执行优先级的中断。如果你是用C语言写的程序,并在中断服务时 using 了寄存组,要注意,两个不同执行优先级的中断服务程序不要 using 同一组寄存器。
看两个问题,如下:
1 在各个中断都是低优先级的时候,如果定时器0的溢出进入中断。在这个中断处理的过程中,外部中断0也被触发了,那么是不是要发生中断嵌套?
2 如果定时器0发生中断的时候,进入中断处理程序,这个时候外部中断1条件触发条件满足了。因为定时器0自然优先级比外部中断1高,那么定时器0的中断处理程序继续执行。假设定时器中断处理程序执行的过程中,外部中断1的触发。条件消失了,那么等定时器0的中断处理完后,程序还是会进入外部中断1处理程序吗?
答案1:在IP事先设置了外部中断0的优先级的情况下,CUP会中止定时器0的中断服务,进入外部中断0服务程序,执行完以后再回到定时器0中断服务程序。否则不会。
答案2:肯定会进入中断的;外部中断1的触发条件满足后会置位外部1的中断标志,即使后来外部中断1的触发条件消失了,也不会清除已置位的中断标志,所以等定时器0的中断处理完后,程序判断外部中断的中断标志为1后依然会进入外部中断1处理程序的,只有在外部中断1处理程序中执行reti指令才会硬件清除外部中断1的中断标志(这也正是为什么中断返回使用reti指令而不可以用ret替换的原因)...
原文地址:https://www.cnblogs.com/baiji/p/10737222.html