找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 35086|回复: 50
打印 上一主题 下一主题
收起左侧

蓝桥杯单片机设计与开发赛前学习准备

  [复制链接]
回帖奖励 2 黑币 回复本帖可获得 2 黑币奖励! 每人限 1 次(中奖概率 10%)
跳转到指定楼层
楼主
ID:231500 发表于 2018-2-11 22:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 13122031726 于 2018-2-13 16:29 编辑

原文出处:Bkoak’s blog  www点bkoak点com/2018/02/11/lanqiao1/

蓝桥杯单片机设计与开发所需设备:
CT107D实验平台、IAP15F2K60S2转接芯片(或最小系统板)

  • 必备开发软件:
已注册Keil5、 STC-ISP-v6.86

注册步骤:①打开keil5→File→License Mana…                   ②复制CID
              

③打开keil5注册机,按图示选择,并粘贴入自己的CID码,复制最下面的LIC码
④返回License Mana…界面,将LIC码粘贴,并点击Add LIC
软件安装注意事项
①下载过程中请关闭所有杀毒软件
②第一次开启keil5,及keil5注册机(有奇妙的音乐建议关闭声音)时,请使用管理员模式运行。

  • 事前准备
1)STC-ISP-v6.86使用说明
①芯片及串口选择                                                                              ②开发辅助功能(范例程序、定时器、延时计算器等)
                                                         

2)keil5工程创建说明
①导入头文件(该操作只需一次)     选择isp软件keil仿真选项点击图中所示位置,选择keil安装目录导入。
                       

②创建keil工程
(1)自定位置创建工程所需文件夹(中英文皆可)。
(2)打开Keil5→Project→New uVision Project
(3)选择芯片(按图示选择,确认后弹出对话框选是)
(4)选择生成HEX文件 点击菜单 Project→Options for Target→Output(勾选图示内容)
(5)创建C程序 点击菜单 File→New→将其保存在工程目录以“xxx.c”命名→导入工程文档中(见图)
              

赛前学习的准备工作到此就大致结束了,下一章内容将简单介绍主要学习内容及相关资料下载。



新建了答疑群:693211073,即针对本次蓝桥杯答疑,也解答一些单片机相关咨询,将来也会开源一些我正在做的设计到该论坛,我的博客,以及交流群里,希望论坛管理员也能支持一下。(群新建,希望来一些朋友帮忙管理)

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏5 分享淘帖 顶3 踩
回复

使用道具 举报

沙发
ID:231500 发表于 2018-2-11 22:17 | 只看该作者
争取三月底比赛前更新完成省赛全部内容
回复

使用道具 举报

板凳
ID:231500 发表于 2018-2-11 23:35 | 只看该作者
按题目格式搜索可以找到后续内容,同时在博客持续更新。
回复

使用道具 举报

地板
ID:231500 发表于 2018-2-12 00:01 | 只看该作者
上文芯片型号写错应该是IAP15F2K61S2,I/O位置功能全同STC15F2K60S2
回复

使用道具 举报

5#
ID:231500 发表于 2018-2-12 00:48 | 只看该作者
本帖最后由 13122031726 于 2018-2-12 01:17 编辑

第二部分

原文出处:Bkoak’s blog  www点bkoak点com/2018/02/11/lanqiao2/

  • CT107D单片机综合实训平台

不推荐官方购买该平台,很贵,建议用同类产品替换,或买二手的。
以下简称实训平台,该文章只介绍比赛使用部分模块。
(1) 单片机芯片
配置 40 脚 51 系列单片机插座(比赛使用IAP15F2K61S2转接芯片);
  
全I/O兼容8051系列,为保留P3.6,P3.7的WR,RD功能由P4.2,P4.4替换。
(2) 显示模块
配置8路led发光二极管;
配置8位8段共阳数码管;
(2) 输入/输出模块
配置 4×4 键盘矩阵,其中四个按键可通过跳线配置为独立按键;
配置继电器、蜂鸣器;
(3) 传感模块
配置红外一体头 1838 及红外发射管;
配置一个霍尔传感器;
配置数字温度传感器 DS18B20;
(4)存储/AD、DA/时钟模块
配置 EEPROM 芯片 AT24C02;
配置 PCF8591A/D、D/A 芯片,内含 8 位 4 通道 A/D 转换、单通道 D/A 转换;
配置时钟芯片DS1302;
   
(5)USB转TTLIO拓展
配置 ch341进行USB转串口进行程序下载;
配置4片74HC573进行IO复用;
配置138译码器与74HC02四2输入或非门控制573片选;
      

实训平台布局图
各个基本功能模块在实验板上的布局如下图所示。
                原理图文末下载
需使用跳线说明(详见开发板)(1) 红外功能选择 跳线 ( J2 )
5、3 短接,6、4 短接:选择红外发射/接收功能
(2) USB 功能选择(J4 )
1、2 短接:选择 UART 功能,USB 接口用作串行通信
(3) 按键功能选择(J5 )
1、2 短接:选择 4×4 键盘功能
2、3 短接:选择 4 个独立按键功能
(4) 外设访问方式选择(J13 )
2、3 短接:选择 I/O 口直接控制方式
(5) 外设访问方式选择 (J15 )
1、2 短接:选择 51 系列单片机

管脚说明见PDF:P48文末下载
CH341驱动文末下载
  • 通过省赛必须掌握内容
1)片上功能(见datasheet文末下载
①定时器
②中断
③捕获
④串口
⑤外部中断
⑥EEPROM
⑦ADC
2)重点外设功能(见后续文章)
①键盘
②数码管、LED
③继电器、蜂鸣器
④DS1302、AT24C02
⑤DS18B20
⑥PCF8591
2)额外功能(视情况更新)
①串口通信
②LCD1602
③红外收发
④霍尔元件频率测算
⑤超声波测距

本章资料下载地址见原文

开发平台简介及主要学习内容的介绍到此就大致结束了,下一章将正式开始蓝桥杯单片机教学。

评分

参与人数 1黑币 +8 收起 理由
arthuryu + 8 赞一个!

查看全部评分

回复

使用道具 举报

6#
ID:263980 发表于 2018-2-12 09:11 | 只看该作者
参加过,但是觉得很水
回复

使用道具 举报

7#
ID:231500 发表于 2018-2-12 12:56 来自手机 | 只看该作者
奋斗de小青年 发表于 2018-2-12 09:11
参加过,但是觉得很水

入门级嘛。学校交钱的就去一下。主要写一下得奖的方法。聊胜于无
回复

使用道具 举报

8#
ID:231500 发表于 2018-2-12 21:20 | 只看该作者
本帖最后由 13122031726 于 2018-2-12 21:40 编辑

第三部分

原文出处:Bkoak’s blog  www点bkoak点com/2018/02/12/lanqiao3/


  • CT107D实验平台操作关键——器件原理部分
CT107D实验平台区别于其他51单片机及开发平台的区别就在于该平台在I/O口复用上的设计。
其中的关键就在于对138译码器、四2输入或非门、573三态锁存器的操作上,本章内容就将着重介绍这三部分的使用方法。
1)3线-8线译码器 —— 74HC138(电气参数见datasheet文末下载
引脚分布                 芯片实物引脚顺序见datasheet。
138译码器的工作原理是选通端(G1)为高电平,另两个选通端((G2A)和(G2B))为低电平时,可将地址端(A、B、C)的二进制编码在一个对应的输出端以低电平译出。
引出端符号:
A、B、C 译码地址输入端(接单片机IO口)
G1 选通端(接电源或单片机IO口)
(G2A)、(G2B) 选通端(低电平有效)
Y0~Y7 译码输出端(接受控器件)
在该平台A、B、C 译码地址输入端接入单片机P2寄存器中的P25、P26、P27三个端口。
真值表:
其中G2* =(G2A) + (G2B)、L = 低电平、H = 高电平、X = 任意
推荐代码写法:

  1. //如果需要使用P20~P25端口建议如下写法
  2. P2 = ((P2&0x1f)|0xA0); //这种写法只改变后P2高三位的状态,不改变前面低五位的状态。
  3. //P2&0x1f的操作是保留低五位状态将高三位置0,0x1f = 00011111  和  0xA0 = 10100000进行或运算  
  4. //最终 P2 = 0xBF = 10111111
  5. P2 = 0xA0; //如无需使用P20~P25端口可直接对P2寄存器进行操作
复制代码





2)四2输入或非门(OC)—— 74HC02(电气参数见datasheet文末下载
引脚分布                 芯片实物引脚顺序见datasheet。
四2输入或非门(OC)为四组 2 输入端或非门(正逻辑)。
引出端符号:
A1-A4或非门输入端(接单片机IO口或GND)
B1-B4或非门输入端(接单片机IO口或GND)
Y1~Y4 或非门输出端(接受控器件)
在该平台A1-A4或非门输入端接入GND或P36(通过跳线选择),B1-B4或非门输入端接入GND,Y1~Y4 或非门输出端接四片573(LE)锁存使能端。
功能表:
其中A、B、Y为或非门输入输出端、L = 低电平、H = 高电平
未接单片机IO口不涉及直接操作代码段。



3)八进制3态非反转透明锁存器 —— 74HC573(电气参数见datasheet文末下载
引脚分布                 芯片实物引脚顺序见datasheet。
573三态锁存器是标准CMOS输入/输出器件,当锁存使能端为高时,该器件为透明态(输入同输出);当锁存使能端为低时,在符合建立时间和保持时间的数据会被锁存。
引出端符号:
D1-D8输入端(接单片机IO口)
Q1-Q8输出端(接受控器件)
LE锁存使能端(接控制器件)
OE输出使能端(接GND)
在该平台D1-D8输入端接入单片机P0,Q1-Q8输出端接受控元器件,LE锁存使能端接四2或非门输出端。
功能表:
其中Z为高阻态、L = 低电平、H = 高电平、X 不用关心
  • CT107D实验平台操作关键——代码操作部分
  1. P2 = ((P2&0x1f)|0xA0); //操作138译码器ABC输入端为 101,Y0~Y7输出为 11111011,其中Y5为 L
  2. /*
  3. CT107D实验平台中 Y4~Y7分别对应控制或非门输出端Y1~Y4
  4. 通过或非门选择分别控制以下四部份的573锁存器
  5.     ①LED发光二极管
  6.     ②达林顿管——(电机、继电器、蜂鸣器)
  7.     ③8位8段数码管段选
  8.     ④8位8段数码管位选
  9. */
  10. P0 = 0x00; //控制②号573锁存器驱动,达林顿管ULN2003输出0xff,控制电机、继电器、蜂鸣器等原件
复制代码




本章资料下载地址见原文。

开发平台关于138译码器、四2输入或非门、573三态锁存器的介绍到此就大致结束了,下一章将开始讲解消音,点亮LED及流水灯。

回复

使用道具 举报

9#
ID:231500 发表于 2018-2-12 23:31 | 只看该作者
第三部分可以快点通过下审核吗,,
回复

使用道具 举报

10#
ID:231500 发表于 2018-2-13 15:11 来自手机 | 只看该作者
本帖最后由 13122031726 于 2018-2-13 16:30 编辑

新建了答疑群:693211073,即针对本次蓝桥杯答疑,也解答一些单片机相关咨询,将来也会开源一些我正在做的设计到该论坛,我的博客,以及交流群里,希望论坛管理员也能支持一下。(群新建,希望来一些朋友帮忙管理)
回复

使用道具 举报

11#
ID:32289 发表于 2018-2-13 20:12 | 只看该作者
这个只有纯属支持一下子了,没有这个技术。
回复

使用道具 举报

12#
ID:231500 发表于 2018-2-13 23:42 | 只看该作者
rrqxx 发表于 2018-2-13 20:12
这个只有纯属支持一下子了,没有这个技术。

互相交流学习嘛,大家都一样,技术都是时间经验积累的,不能一蹴而就。
回复

使用道具 举报

13#
ID:231500 发表于 2018-2-14 00:43 | 只看该作者
【蓝桥杯单片机设计与开发】(4)消音,点亮LED及流水灯的操作

本章我们的主要内容分为三点
①   介绍如何开启单片机电源后使其蜂鸣器、继电器停止工作,因为开发板的设计中,开机后蜂鸣器是直接启动的,需要通过操作寄存器使之关闭,不然会很影响工作。
②   点亮LED,并对LED进行一些简单的操作说明。
③   教学流水灯程序的两种写法。
  • 一、消音、关闭继电器
首先,我们先看这一部分的原理图
大家可以从我第二篇博文下载原理图自行查看,蜂鸣器和继电器都是由达林顿管ULN2003来控制的,因为这两个器件都属于大电流器件不能通过单片机的IO口直接驱动,所以外加了电流放大的装置,我们可以将ULN2003看做一个反向器或者是一个非门,所有TTL信号过来都会被取反取非。
继电器和蜂鸣器分别接在ULN2003的out6和out7,这两个管脚的输入端in6和in7分别由U9这片573三态锁存器的Q6和Q7控制,从上一章内容可知操控这片573芯片的方式,在看到 继电器和蜂鸣器 另一端都是接的电源,也就是当out6和out7输出高电平“1”时才能使这两个元件停止工作,继电器端并联的二极管为续流二极管用来保护元件不被感应电压击穿或烧坏。

消音、关闭继电器代码如下:
  1. #include “stc15f2k60s2.h”

  2. void main()//消音、关闭继电器程序
  3. {
  4.    while(1)
  5.    {
  6.    P2 = ((P2&0x1f)|0xA0); //选中控制蜂鸣器、继电器的573芯片
  7.    P0 = 0X00; //输入0000 0000,通过ULN2003取非,输出1111 1111,
  8.    //使受控源两端电势差为0
  9.    }
  10. }
复制代码

工程文件文末下载

  • 三、流水灯的两种写法
在这里为大家介绍两种流水灯的写法,也是单片机入门最简单的一个程序,分别使用位运算的左右移符号“<<”、“>>”符号和调用左右移函数“_crol_”、“_cror_”两种操作方式,原理介绍见代码注释,其中我们需要一个500毫秒的软件延时函数可以直接使用我第一篇文章中介绍的方法直接用软件生成。
流水灯①代码如下:
  1. #include “stc15f2k60s2.h”
  2. #include “intrins.h”//因为延时函数中含有_nop_指令,
  3.                     //需要用到这个头文件,同时流水灯2
  4.                     //中的左右移函数也是从中调用的

  5. void main()//流水灯1程序
  6. {
  7.    unsigned char i;

  8.    P2 = ((P2&0x1f)|0xA0);
  9.    P0 = 0X00; //先进行消音处理,蜂鸣器声音刺耳。
  10.    P2 = ((P2&0x1f)|0x80);//选择控制LED的573
  11.    P0 = 0x00;//点亮八个LED

  12.    while(1)//循环条件永远为真,以下程序一直执行下去。
  13.    {
  14.       for(i = 0; i < 8; i++)
复制代码


流水灯②代码如下
  1. <span style="font-family: Tahoma, &quot;Microsoft Yahei&quot;, Simsun;">#include</span><span style="font-family: Tahoma, &quot;Microsoft Yahei&quot;, Simsun;"> </span><span style="font-family: Tahoma, &quot;Microsoft Yahei&quot;, Simsun; white-space: normal;">“stc15f2k60s2.h”</span>
复制代码
以上代码中的Delay500ms();均是为了使led亮的时间能够让人用肉眼识别。

本章关于消音、点亮LED和流水灯的编程的介绍到此就大致结束了,下一章将开始讲解独立按键和矩阵键盘的程序编写。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak's blog  http://www.bkoak.com/2018/02/13/lanqiao4/

联系博主
邮箱:wjf418224424@aliyun.com
QQ交流群:693211073



消音、点亮LED例程.rar

23.75 KB, 下载次数: 10, 下载积分: 黑币 -5

本章例程

回复

使用道具 举报

14#
ID:231500 发表于 2018-2-14 10:19 来自手机 | 只看该作者
本帖所有例程,博客原文中均可直接下载,没有黑币的朋友可以去原文下载
回复

使用道具 举报

15#
ID:274729 发表于 2018-2-14 10:52 | 只看该作者
谢谢楼主
回复

使用道具 举报

16#
ID:231500 发表于 2018-2-14 13:56 | 只看该作者
本帖是不限于蓝桥杯,主要针对单片机入门问题进行讲解,有问题的朋友可以直接留言
回复

使用道具 举报

17#
ID:231500 发表于 2018-2-14 17:20 | 只看该作者
【蓝桥杯单片机设计与开发】(5)独立按键和矩阵键盘的应用

本章我们的主要内容分为两部分
①  独立按键的编程及使用
②  矩阵按键的扫描方式以及编程原理
PS:因为比赛使用的是IAP15的转接板,该开发板不可使用按键处罚外部中断的方式检测按键,故不做讨论,
同时IAP15芯片的WR/RD功能不是P36/P37引脚功能,故用P42/P44引脚代替
故提供两种程序开头写法:
第一种使用reg52.h开头的程序
  1. #include “stc15f2k60s2.h”

  2. void main()//消音、关闭继电器程序
  3. {
  4.    while(1)
  5.    {
  6.       P2 = ((P2&0x1f)|0xA0); //选中控制蜂鸣器、继电器的573芯片
  7.       P0 = 0X00; //输入0000 0000,通过ULN2003取非,输出1111 1111,
  8.       //使受控源两端电势差为0
  9.    }
  10. }
复制代码



第二种使用stc15f2k60s2.h开头的程序
  1. #include “stc15f2k60s2.h”//该文件已定义P4寄存器故下方无需重复定义

  2. sbit P3_6 = P4^2;//位定义用 P3_6 在程序中替换 P4^2的功能
  3. sbit P3_7 = P4^4;//同上
复制代码


本文均以stc15为开头的程序做示例。
  • 1ms延时程序(11.0592M晶振
  1. void Delay_1_ms(unsigned int n)//@11.0592MHz
  2. {
  3.    unsigned char i,j;
  4.    for(i = 0;i > n;i--)
  5.    {
  6.       _nop_();
  7.       _nop_();
  8.       _nop_();
  9.       i = 11;
  10.       j = 190;
  11.       do
  12.       {
  13.          while (--j);
  14.       } while (--i);
  15.    }
  16. }
复制代码


该函数为后续例程中所使用的延时函数。

  • 独立按键
首先,我们依然是先来看这一部分的电路原理图
首先我们将左下角的端口选择用跳线帽将2、3连在一起,我们可以看到按键S4~S7分别连接的是单片机的P30~P33端口,本章我们就用按键来控制上一章教学的LED不同的点亮方式来体现按键的功能。(为节约空间,此处仅以S7按键讲解
示例程序:
  1. #include “stc15f2k60s2.h”
  2. #include “intrins.h”

  3. #define LED P0  //宏定义,在程序中可用LED代替P0使用

  4. sbit key0 = P3^0; //对S7按键所接端口进行位定义

  5. void Delay_1_ms(unsigned int n);

  6. void main()
  7. {
  8.    P2 = ((P2&0x1f)|0xA0); //不再重复解释,前两章已解释多次
  9.    P0 = 0X00;
  10.    P2 = ((P2&0x1f)|0x80); //因为要使用LED故选择到该区域
  11.    P0 = 0Xff;//先关闭LED
  12.       
  13.    while(1)
  14.    {
  15.    if(key0 == 0)//档S7按键按下GND与P30导通P30得到低电平的信号
  16.       {
  17.          Delay_1_ms(10);//延时消抖,去除按键机械抖动带来的干扰因素
  18.          if(key0 == 0)//再次确认按键按下
  19.          {
  20.             LED = 0X00;//按键按下 点亮所有LED
  21.             while(!key0);//判断按键是否松开,如果为松开则停在此处等待
  22.          }
  23.          LED = 0xff;//按键松开,熄灭所有LED
  24.       }
  25.    }
  26. }
复制代码



以上就是独立按键的编程过程。
  • 矩阵键盘
现在我们将选择端口,转接到12上如原理图所示,将按键分为四行四列,然后编程中我们分别进行“行”扫描和“列”扫描,进行标志位的标致,就能判断出所按下的按键的是哪一个,详细方法见一下程序。
扫描函数
  1. void keyscan()
  2. {
  3.    BUFFER = 0X0F; P3_6 = 0; P3_7 = 0;
  4.    /*首先我们进行列扫描,将P34~P37都置零,其中要注意,开发板上用
  5.    P42、P44替换了P36、P37故我们要单独将这两个IO置零*/
  6.    if(BUFFER != 0X0F)// “!=”表示档P3不等于0x0f时条件为真
  7.    {
  8.       Delay_1_ms(10); //延时消抖
  9.       if(BUFFER != 0X0F)//再次判断
  10.       {
  11.          switch(BUFFER)
  12.          /*当按键按下行中值发生变化得到一个BUFFER,
  13.          生成一个新的标志位*/
  14.          {
  15.             case 0X07: key_value = 1; break;
  16.             case 0X0B: key_value = 5; break;
  17.             case 0X0D: key_value = 9; break;
  18.             case 0X0E: key_value = 13; break;
  19.          }

  20.          BUFFER = 0XF0; P3_6 = 1; P3_7 = 1;
  21.          /*我们在进行行扫描,原理同上下方进行判断第几行
  22.          按键按下,结合列标志位生成新的行列标志位。*/
  23.          if(P3_7 == 0) key_value += 0; while(P3_7 == 0);
  24.          if(P3_6 == 0) key_value += 1; while(P3_6 == 0);
  25.          if(BUFFER == 0XD0) key_value += 2; while(BUFFER == 0XD0);
  26.          if(BUFFER == 0XE0) key_value += 3; while(BUFFER == 0XE0);
  27.       }
  28.    }
  29. }
复制代码



主函数
  1. #include “stc15f2k60s2.h”
  2. #include “intrins.h”

  3. #define LED P0  //宏定义,在程序中可用LED代替P0使用
  4. #define BUFFER P3  //宏定义,在程序中用BUFFER代替P3端口

  5. sbit P3_6 = P4^2;//位定义用 P3_6 在程序中替换 P4^2的功能
  6. sbit P3_7 = P4^4;//同上

  7. void Delay_1_ms(unsigned int n);
  8. void keyscan();//键盘扫描函数

  9. void main()
  10. {
  11.    P2 = ((P20x1f)|0xA0); //不再重复解释,前两章已解释多次
  12.    P0 = 0X00;
  13.    P2 = ((P20x1f)|0x80); //因为要使用LED故选择到该区域
  14.    P0 = 0Xff;//先关闭LED
  15.       
  16.    while(1)
  17.    {
  18.       keyscan();//键盘扫描函数
  19.       switch(key_value)
  20.       {
  21.          case  1: LED = 0x00; break;
  22.          case  2: LED = 0x12; break;
  23.          case  3: LED = 0xab; break;
  24.          case  4: LED = 0xfe; break;
  25.          case  5: LED = 0x08; break;
  26.          case  6: LED = 0x0c; break;
  27.          case  7: LED = 0xa5; break;
  28.          case  8: ; break;
  29.          case  9: ; break;
  30.          case 10: ; break;
  31.          case 11: ; break;
  32.          case 12: ; break;
  33.          case 13: ; break;
  34.          case 14: ; break;
  35.          case 15: ; break;
  36.          case 16: ; break;//只定义了一部分按键功能,其余大家可以随意发挥
  37.          case 17: ; break;//功能不限于点灯
  38.       }
  39.    }
  40. }
复制代码



这些就是关于按键部分的内容部,最后再给大家留一道练习题,用以熟练按键的编程操作。
习题:用四个独立按键和八个LED做一个八位二进制加法器,一个按键表示“+1”,一个按键表示“-1”,一个按键表示“清零”,一个按键表示“清零前最后一次显示的数”。
如若黑币不足可前往原文地址下载
本章关于独立按键和矩阵键盘的编程的介绍到此就大致结束了,下一章将开始讲解定时器0和动态数码管的程序编程序。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak's blog  http://www.bkoak.com/2018/02/14/lanqiao5/

联系博主
邮箱:wjf418224424@aliyun.com
QQ交流群:693211073

独立按键、矩阵键盘.rar

27.92 KB, 下载次数: 10, 下载积分: 黑币 -5

例程

回复

使用道具 举报

18#
ID:231500 发表于 2018-2-14 21:27 | 只看该作者
内个,第五篇通过下啊,下午就更新了的
回复

使用道具 举报

19#
ID:231500 发表于 2018-2-15 13:21 来自手机 | 只看该作者
今年过年,可能停更一天
回复

使用道具 举报

20#
ID:231500 发表于 2018-2-16 01:51 | 只看该作者
本章我们的主要内就只有一个,基本属于纯概念性问题,讲解中断系统的工作原理,看过留个映像,每次用到的时候在翻出来结合理解,多几次就明白了,不求甚解为最佳。(重中之重)
在此做一个解释说明,本来是想在第六章安排,静态数码管以及定时器的应用,但是我写到定时器,发现内容量很大,在写了一半定时器后又觉得不得不先为大家引入中断系统这个概念,只有有了这个概念才能更好的理解运用定时器,故本章内容改为讲解中断系统的工作原理
本章内容比较多,希望大家多结合手中资料进行自我学习,尤其是定时器部分一定要自己在好好看看datasheet,蓝桥杯教学系列第一章内有下载地址。
  • 中断系统
1)中断的概念:这是大家需要在脑子里有的一个印象,我们大致可以将中断描述成CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程我们称为中断。
2)中断源:我们将引起CPU中断的根源或原因,称为中断源。中断源向CPU提出的处理请求,称为中断请求或中断申请。(传统的51单片机有5个中断源,而我们使用的IAP15有14个中断源)
3)使用中断我们一定要知道中断优先级和中断嵌套这两个概念。
①中断优先级:当有几个中断源同时申请中断时,那么就存在CPU先响应哪个中断请求的问题?为此,CPU要对各中断源确定一个优先等级,称为中断优先级。中断优先级高的中断请求优先响应。
②中断嵌套:中断优先级高的中断请求可以中断CPU正在处理的优先级更低的中断服务程序,待完成了中断优先权高的中断服务程序之后,再继续执行被打断的优先级低的中断服务程序,这就是中断嵌套。
下图为中断系统的一个流程演示:
我们使用的IAP15F2K61S2单片机的中断系统有14个中断源,2个优先级,可实现二级中断服务嵌套。由片内特殊功能寄存器中的中断允许寄存器IE、IE2、INT_CLKO控制CPU是否响应中断请求;由中断优先级寄存器IP、IIP2安排各中断源的优先级;同一优先级内2个以上中断同时提出中断请求时,由内部的查询逻辑确定其响应次序。
中断请求标志:IAP15F2K61S2单片机的10个中断源的中断请求标志分别寄存在TCON、SCON、PCON、S2CON、ADC_CONTR、SPSTAT、CCON中,其中,外部中断2(/INT2)、外部中断3(/INT3)和外部中断4(/INT4)的中断请求标志位被隐藏起来了,对用户是不可见的。当相应的中断被响应后或(EXn)=0(n=2、3、4),这些中断请求标志位会自动被清0;定时器T2的中断请求标志位也被隐藏起来了,对用户是不可见的,当T2的中断被响应后或(ET2)=0,这些中断请求标志位会自动被清0。
IAP15F2K61S2单片机的中断请求
中断允许的控制:计算机中断系统有两种不同类型的中断:一类称为非屏蔽中断,另一类称为可屏蔽中断。IAP15F2K61S2单片机的14个中断源都是可屏蔽中断,其中断系统内部设有3个专用寄存器(IE、IE2、INT_CLKO)用于控制CPU对各中断源的开放或屏蔽。
中断优先的控制:IAP15F2K61S2单片机除外部中断2(/INT2)、外部中断3(/INT3)、定时器T2中断和外部中断4(/INT4)为固定最低优先级中断外,其他中断都具有2个中断优先级,可实现二级中断服务嵌套。IP、IP2为中断优先级寄存器,锁存各中断源优先级控制位。
自然优先级排序
中断源                                                       同级自然优先顺序
外部中断0                                                  最高
定时器T0
中断
外部中断1
定时器T1中断
串行口中断
A/D转换中断
LVD中断
PCA中断
串行口2中断
SPI中断
外部中断2
外部中断3
定时器T2中断
外部中断4                                                    最低
IAP15F2K61S2单片机中断响应
中断响应:是CPU对中断源中断请求的响应,包括保护断点和将程序转向中断服务程序的入口地址(通常称矢量地址)。CPU并非任何时刻都响应中断请求,而是在中断响应条件满足之后才会响应。
中断服务与中断返回:中断服务与中断返回就是通过执行中断服务程序完成的。中断服务程序从中断入口地址开始执行,到返回指令“RETI”为止,一般包括四部分内容,其结构是:保护现场,中断服务,恢复现场、中断返回。
保护现场:用入栈操作指令将需保护寄存器的内容压入堆栈。
中断服务:中断服务程序的核心部分,是中断源中断请求之所在。
恢复现场:在中断服务结束之后,中断返回之前,用出栈操作指令将保护现场中压入堆栈的内容弹回到相应的寄存器中,注意弹出顺序必须与压入顺序相反
中断返回:中断返回是指中断服务完成后,计算机返回原来断开的位置(即断点),继续执行原来的程序。中断返回由中断返回指令RETI来实现。

本章无需要下载文件,请各位自行查阅第一章下载资料中的STC15.pdf文档。

本章知识点杂且多,文章中我也无法做到全面覆盖,像定时器中断、PCA中断和外部中断有很多的运用方法,文中并没有一一举例,只是从概念和我们使用的单片机的结构上简单的介绍了中断系统的概念,还需要大家多复习自己多去看看多去想想,在实践中理解运用,将书面中的东西化作自己脑海中的东西。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak's blog[url]http://www.bkoak.com/2018/02/12/lanqiao3/[/url]

联系博主
邮箱:wjf418224424@aliyun.com
QQ交流群:693211073

回复

使用道具 举报

21#
ID:231500 发表于 2018-2-16 21:50 | 只看该作者
本章我们的主要内容也只有一个,同样基本属于纯概念性问题,讲解定时器/计数器(Timer0/1)的功能模式和使用原理,这部分内容如果单纯只看是比较难理解的,希望大家结合前几张的内容来自己试着改写一下以前的程序,将之前程序中的延时程序用定时器来做替换完成同样的功能。
定时器(Timer0/1)
首先我们需要知道定时器Timer需要使用的寄存器有哪些:
我们先来简单的说一下TMOD、 TCON两个寄存器的功能和区别:
1)TMOD:就是运行模式配置寄存器,也就是配置定时器是工作在定时器状态还是计数器状态,以及定时器的工作模式是13位定时器,16位定时器,双8位定时器自动重装,还是双8位定时器等等。
2)TCON:就是定时/计数器的控制寄存器,包括控制定时器的的启动与停止,是否允许外部的中断请求判断定时器是否已经溢出等等。
简而言之,一句话2个寄存器 一个是配置模式功能 一个是控制功能,具体的运用理解还需要大家多看多用。
定时器/计数器0/1控制寄存器TCON
TCON为定时器/计数器T0、T1的控制寄存器,同时也锁存T0、T1溢出中断源和外部请求中断源等,TCON格式如下:
TCON为定时器/计数器中断控制寄存器(可位寻址)
各寄存器功能请见datasheet P592。
定时器/计数器0/1工作模式寄存器TMOD
定时和计数功能由特殊功能寄存器TMOD的控制位C/T进行选择,TMOD寄存器的各位信息。
可以看出,2个定时/计数器有4钟操作模式,通过TMOD的M1和M0选择。2个定时/计数器模式0、1和2都相同,模式3不同,各模式下的功能请参见datasheet P593。
定时器0/1的中断控制寄存器:IE和IP
IE:中断允许寄存器(可位寻址)
EA:CPU的总中断允许控制位,EA = 1,CPU开放中断,EA = 0,CPU屏蔽所有的中断申请。
ET1:定时/计数器T1的溢出中断允许位,ET1 = 1,允许T1中断,ET1 = 0禁止T1中断。
ET0:同上。
IP:中断优先级控制寄存器(可位寻址)
PT1:           定时器1中断优先级控制位。
当PT1 = 0时,定时器1中断为最低优先级中断(优先级0)
当PT1 = 1时,定时器1中断为最高优先级中断(优先级1)
PT0:           定时器1中断优先级控制位。
当PT0 = 0时,定时器0中断为最低优先级中断(优先级0)
当PT0 = 1时,定时器0中断为最高优先级中断(优先级1)
注意事项详见datasheet P597。
辅助寄存器AUXR
普通8051单片机是12T的机器周期,STC15的是1T的这个辅助寄存器的功能就是用来选择STC15工作在1T还是12T的模式下。
定时器/计数器0工作模式
通过对寄存器TMOD中的M1(TMOD.1)、(TMOD.0)的设置,定时器/计数器0有4种不同的工作模式。
模式0(16位自动重装载模式)(只介绍该模式,其余三种模式见datasheet
定时器/计数器0工作模式0(16位自动重装载模式)
M1和M0:方式选择控制位
C/ T:功能选择位。
1:计数器功能(对T0或T1引脚的负跳变进行计数)。
0:定时器功能(对时钟周期进行计数)。
GATE:门控位。GATE用于选通控制。
1:INTX为高电平且TRX置位时,启动定时器工作。
0:每当TRX置位时,就启动定时器工作。
注意:TMOD寄存器不能进行位寻址,设置时只 能对整个寄存器赋值。
IAP15F2K61S2的定时器有两种计数速率:一种是12T模式,每12个时钟加1;另一种是1T模式,每个时钟加1。
T0和T1的速率分别由特殊功能寄存器AUXR中的T0x12和T1x12决定。
T0x12=0,T0工作在12T模式;
T0x12=1,T0工作在1T模式。
T1x12=0,T1工作在12T模式;
T1x12=1,T1则工作在1T模式。
定时器0和定时器1分别有2个隐藏的寄存器RL_THx和RL_TLx。RL_THx与THx共有同一个地址,RL_TLx与TLx共有同一个地址。当TRx=0即定时器/计数器被禁止工作时,对TLx、THx写入的内容会同时写入RL_TLx、RL_THx。 当TRx=1即定时器/计数器工作时,对TLx 、THx写入的内容不会写入RL_TLx 、RL_THx。
当定时器工作在模式0时,[TLx,THx]的溢出 不仅置位TFx,而且会自动将[RL_TLx,RL_THx] 的内容重新装入[TLx,THx]。
当T0CLKO=1时,T1/P3.5引脚配置为定时器0的时钟输出CLKOUT0。
当T1CLKO=1时,T0/P3.4引脚配置为定时器1的时钟输出CLKOUT1。
详细介绍参见datasheet P600。
定时器的最大定时能力
当工作于定时状态时,定时/计数器是对时钟周期进行计数,若对时钟进行12分频,则对每12个时钟周期计数一次。
当晶振频率为6MHz,采用12分频时,计数的单位时间为:
单位时间为:
定时时间为:   TC=XTu
其中,Tu为单位时间,TC为定时时间,X为所需计数次数。
STC15F2K60S2单片机的定时/计数器是加1计数器。因此,不能直接将实际的计数值作为计数初值送入计数寄存器THX、TLX中,而必须将实际计数值以28、216为模求补,以补码作为计数初值设置THX和TLX。
即应装入计数/定时器的初值为:,其中,n=8或16。
例如:已知Tu=2μs,要求定时TC=1ms,则
  • 对方式0和方式1,时间常数为:216-500=65036=FE0CH (THX装入FEH,TLX装入0CH)。
设系统时钟频率为6MHz,12分频时,
  • 8位定时器的最大定时能力为:T=(28 -0)×2μs=512μs
  • 16位定时器的最大定时能力为:T=(216 -0)×2μs=131072μs=131.072ms
最后补充一下,初学阶段对定时器的使用可以直接使用我第一章中所讲的方法直接自动生成所需定时器,用以程序使用,但是还是希望大家理解最重要。
以上是定时器的原理,接下来我们简单运用定时器来写个控制LED量灭的程序:
  1. #include “stc15f2k60s2.h”

  2. #define LED P0

  3. unsigned char t;

  4. void Timer0Init(void);

  5. void main()
  6. {
  7.    P2 = ((P2&0x1f)|0xA0); //不再重复解释,前两章已解释多次
  8.    P0 = 0X00;
  9.    P2 = ((P2&0x1f)|0x80);
  10.    P0 = 0Xff;//先关闭LED

  11.    Timer0Init();
  12.    EA = 1; //打开定时器0
  13.    ET0 = 1;

  14.    while(1)
  15.    {
  16.       if(t == 200)
  17.       {
  18.          LED = ~LED;
  19.          t = 0;//初始化标志位
  20.       }
  21.    }
  22. }

  23. void Timer0Init(void)//5毫秒@11.0592MHz
  24. {
  25.    AUXR |= 0x80;//定时器时钟1T模式
  26.    TMOD &= 0xF0;//设置定时器模式
  27.    TL0 = 0x00;//设置定时初值
  28.    TH0 = 0x28;//设置定时初值
  29.    TF0 = 0;//清除TF0标志
  30.    TR0 = 1;//定时器0开始计时
  31. }

  32. void Timer0() interrupt 1 //定时器0的开启中断1
  33. {
  34.    t++;
  35. }
复制代码



               本章资料下载地址请前往原址。
本章关于定时器的讲解就到此结束了,希望大家多在实践中学习,多用多看,一边动手一边学习,加强自己对知识点的理解。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak's blog  http://www.bkoak.com

联系博主
邮箱:wjf418224424@aliyun.com
QQ交流群:693211073


回复

使用道具 举报

22#
ID:231500 发表于 2018-2-17 19:31 | 只看该作者
郑重声明,请不要复制粘贴代码,第一可能会被网页专制标点符号,导致编译报错,其次复制粘贴达成的效果,可能会让你产生一种我会了的错觉。
本章我们的主要内容是静态数码管和动态数码管的编程,主要内容在程序中讲解,同时会复习一下上一章定时器的内容。
共阴/共阳数码管
首先我们先来了解一下数码管的工作原理,我们普遍将数码管分为两种一种是共阴数码管一种是共阳数码管(我们使用的是两个4位八段共阳数码管)。
所谓八段数码管就是八个发光二极管组成的如上图的结构形态,共阴数码管就是八个LED阴极相连组成公共端接GND,共阳数码管就是八个LED阳极相连组成公共端接VCC。
我们使用的4位八段数码驱动原理也是如此其中八个管脚作为(A,B,C,D,E,F,G,DP)的驱动端口,选择哪一段数码管亮或不亮,我们称之为数码管段选,还有4个管脚作为选择四位数码管中的哪一位亮或不亮的驱动端口,也就是四个公共端,我们称之为数码管位选。
驱动原理
下面我们来看一下我们开发板的驱动原理图是如何驱动这两个四位共阳数码管的。
图中a1、b1……dp1就是数码管的段选端口,com1、com2……com8就是数码管的位选端口,我们可以看到,蓝桥杯开发板的数码管段选和位选都是通过单片机的P0寄存器来驱动的,但是因为P0不能同一时刻既做段选又做位选,所以这里就要通过我们第二章所讲的内容来操作在一个很短的时间内在段选和位选之间切换选择操作。
静态数码管数码管所有公共端接GND或VCC,在该开发板上不是接GND和VCC而是接在两个573三态锁存器上,全亮全灭不单独选择端口操作。优点:不闪烁,亮度高。缺点:占用端口多。
示例程序:
[backcolor=rgb(15, 25, 42) !important][size=1em]
[size=1em]01

[size=1em]02

[size=1em]03

[size=1em]04

[size=1em]05

[size=1em]06

[size=1em]07

[size=1em]08

[size=1em]09

[size=1em]10

[size=1em]11

[size=1em]12

[size=1em]13

[size=1em]14

[size=1em]15

[size=1em]16

[size=1em]17

[size=1em]18

[size=1em]19

[size=1em]20

[size=1em]21

[size=1em]22

[size=1em]23

[size=1em]24

[size=1em]25

[size=1em]26

[size=1em]27

[size=1em]28

[size=1em]29

[size=1em]30

[size=1em]31

[size=1em]32

[size=1em]33

[size=1em]34

[size=1em]35

[size=1em]36

[size=1em]37

[size=1em]38

[size=1em]39

[size=1em]40

[size=1em]41

[size=1em]42

[size=1em]43

[size=1em]44

[size=1em]45

[size=1em]46

[size=1em]47

[size=1em]48

[size=1em]49

[size=1em]50

[size=1em]51

[size=1em]52

[size=1em]53

[size=1em]54

[size=1em]55

[size=1em]56

[size=1em]57

[size=1em][size=1em]/******************/
[size=1em] /* 2018.2.17 */
[size=1em] /* www.bkoak.com */
[size=1em] /* 数码管程序示例 */
[size=1em] /******************/
[size=1em]#include “stc15f2k60s2.h”
[size=1em]#define uchar unsigned char
[size=1em]#define uint unsigned int

[size=1em]uchar code tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,
[size=1em]                  0x82,0xf8,0x80,0x90,0xbf,0xff};
[size=1em]/*这里如果不加code,就会占用寄存器,允许读取和修改寄存器的值,
[size=1em]但是寄存器的数量有限,故我们在这里加上code,将其定义为代码的
[size=1em]类型,只能读取不能修改*/
[size=1em]uchar t = 0,i;

[size=1em]void Timer0Init(void);

[size=1em]void main()
[size=1em]{
[size=1em]   P2 = ((P2&0x1f)|0xa0);//不再重复解释,前两章已解释多次
[size=1em]   P0 = 0x00;
[size=1em]   P2 = ((P2&0x1f)|0xc0);//打开位选选573
[size=1em]   P0 = 0xff;//选择8位数码管
[size=1em]   P2 = ((P2&amp;0x1f)|0xff);//打开段选573

[size=1em]   Timer0Init();
[size=1em]   EA=1;
[size=1em]   ET0=1;

[size=1em]   while(1)
[size=1em]   {
[size=1em]      if(t == 100)
[size=1em]      {
[size=1em]         t = 0;
[size=1em]         P0 = tab;
[size=1em]         i++;
[size=1em]         if(i == 12)
[size=1em]            i = 0;
[size=1em]      }
[size=1em]   }
[size=1em]}

[size=1em]void Timer0Init(void)//5毫秒@11.0592MHz
[size=1em]{
[size=1em]   AUXR |= 0x80;//定时器时钟1T模式
[size=1em]   TMOD &= 0xF0;//设置定时器模式
[size=1em]   TL0 = 0x00;//设置定时初值
[size=1em]   TH0 = 0x28;//设置定时初值
[size=1em]   TF0 = 0;//清除TF0标志
[size=1em]   TR0 = 1;//定时器0开始计时
[size=1em]}

[size=1em]void Timer0() interrupt 1
[size=1em]{
[size=1em]   t++;
[size=1em]}



动态数码管数码管公共端依次扫描接通。优点:占用端口少。缺点:摄像,拍照时闪烁,在同样电压下与静态相比,亮度较低。[backcolor=rgb(15, 25, 42) !important][size=1em]
[size=1em]01

[size=1em]02

[size=1em]03

[size=1em]04

[size=1em]05

[size=1em]06

[size=1em]07

[size=1em]08

[size=1em]09

[size=1em]10

[size=1em]11

[size=1em]12

[size=1em]13

[size=1em]14

[size=1em]15

[size=1em]16

[size=1em]17

[size=1em]18

[size=1em]19

[size=1em]20

[size=1em]21

[size=1em]22

[size=1em]23

[size=1em]24

[size=1em]25

[size=1em]26

[size=1em]27

[size=1em]28

[size=1em]29

[size=1em]30

[size=1em]31

[size=1em]32

[size=1em]33

[size=1em]34

[size=1em]35

[size=1em]36

[size=1em]37

[size=1em]38

[size=1em]39

[size=1em]40

[size=1em]41

[size=1em]42

[size=1em]43

[size=1em]44

[size=1em]45

[size=1em]46

[size=1em]47

[size=1em]48

[size=1em]49

[size=1em]50

[size=1em]51

[size=1em]52

[size=1em]53

[size=1em]54

[size=1em]55

[size=1em]56

[size=1em]57

[size=1em]58

[size=1em]59

[size=1em]60

[size=1em]61

[size=1em]62

[size=1em]63

[size=1em]64

[size=1em]65

[size=1em]66

[size=1em]67

[size=1em]68

[size=1em]69

[size=1em][size=1em]#include "stc15f2k60s2.h"
[size=1em]#include "intrins.h"

[size=1em]#define uchar unsigned char
[size=1em]#define uint unsigned int

[size=1em]uchar code tab[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,
[size=1em]                    0x82,0xf8,0x80,0x90,0xbf,0x7f};
[size=1em]/*这里如果不加code,就会占用寄存器,允许读取和修改寄存器的值,
[size=1em]但是寄存器的数量有限,故我们在这里加上code,将其定义为代码的
[size=1em]类型,只能读取不能修改*/
[size=1em]uchar t = 0;
[size=1em]uchar c5, c6, c7, c8;
[size=1em]uint num = 2330;

[size=1em]void Timer0Init(void);
[size=1em]void display(uchar c5, uchar c6, uchar c7, uchar c8);
[size=1em]void Delay1ms();

[size=1em]void main()
[size=1em]{
[size=1em]   P2 = ((P2&amp;0x1f)|0xa0);//不再重复解释,前两章已解释多次
[size=1em]   P0 = 0x00;
[size=1em]   
[size=1em]   Timer0Init();
[size=1em]   EA=1;
[size=1em]   ET0=1;
[size=1em]   
[size=1em]   while(1)
[size=1em]   {
[size=1em]      if(t == 200)
[size=1em]      {
[size=1em]         t = 0;
[size=1em]         num--;
[size=1em]         c5 = (num / 1000) % 10;
[size=1em]         c6 = (num / 100) % 10;
[size=1em]         c7 = (num /10) % 10;
[size=1em]         c8 = num % 10;
[size=1em]      }
[size=1em]      display(c5, c6, c7, c8);
[size=1em]   }
[size=1em]}

[size=1em]void display(uchar c5, uchar c6, uchar c7, uchar c8)
[size=1em]{
[size=1em]   P2 = ((P2&0x1f)|0xc0);
[size=1em]   P0 = 0x80;
[size=1em]   P2 = ((P2&0x1f)|0xff);
[size=1em]   P0 = tab[c8];
[size=1em]   Delay1ms();

[size=1em]   P2 = ((P2&0x1f)|0xc0);
[size=1em]   P0 = 0x40;
[size=1em]   P2 = ((P2&0x1f)|0xff);
[size=1em]   P0 = tab[c7];
[size=1em]   Delay1ms();

[size=1em]   P2 = ((P2&0x1f)|0xc0);
[size=1em]   P0 = 0x20;
[size=1em]   P2 = ((P2&0x1f)|0xff);
[size=1em]   P0 = tab[c5];
[size=1em]   Delay1ms();

[size=1em]   P2 = ((P2&0x1f)|0xc0);
[size=1em]   P0 = 0x10;
[size=1em]   P2 = ((P2&0x1f)|0xff);
[size=1em]   P0 = tab[c6];
[size=1em]   Delay1ms();
[size=1em]}



本章资料下载地址见原文。
本章的内容到这里就结束了,最后给大家留一个题目,可以回去思考,后面有时间也会更新讲解,使用单片机定时器制作一个以00-00-00为初始可以通过四个独立按键进行时间修改的24小时制时钟。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak’s blog http://www.bkoak.com

联系博主
邮箱:wjf418224424@aliyun.com
QQ交流群:693211073

回复

使用道具 举报

23#
ID:231500 发表于 2018-2-18 00:34 | 只看该作者
上一贴直接复制原文可能导致网页编码出问题,程序显示不正常万望两节

在此补发
【蓝桥杯单片机设计与开发】(8)静态数码管/动态数码管的编程作者: bkoak 分类: 蓝桥杯 发布时间: 2018-02-17 19:19 编辑


郑重声明,请不要复制粘贴代码,第一可能会被网页专制标点符号,导致编译报错,其次复制粘贴达成的效果,可能会让你产生一种我会了的错觉。
本章我们的主要内容是静态数码管和动态数码管的编程,主要内容在程序中讲解,同时会复习一下上一章定时器的内容。
共阴/共阳数码管
首先我们先来了解一下数码管的工作原理,我们普遍将数码管分为两种一种是共阴数码管一种是共阳数码管(我们使用的是两个4位八段共阳数码管)。
所谓八段数码管就是八个发光二极管组成的如上图的结构形态,共阴数码管就是八个LED阴极相连组成公共端接GND,共阳数码管就是八个LED阳极相连组成公共端接VCC。
我们使用的4位八段数码驱动原理也是如此其中八个管脚作为(A,B,C,D,E,F,G,DP)的驱动端口,选择哪一段数码管亮或不亮,我们称之为数码管段选,还有4个管脚作为选择四位数码管中的哪一位亮或不亮的驱动端口,也就是四个公共端,我们称之为数码管位选。
驱动原理
下面我们来看一下我们开发板的驱动原理图是如何驱动这两个四位共阳数码管的。
图中a1、b1……dp1就是数码管的段选端口,com1、com2……com8就是数码管的位选端口,我们可以看到,蓝桥杯开发板的数码管段选和位选都是通过单片机的P0寄存器来驱动的,但是因为P0不能同一时刻既做段选又做位选,所以这里就要通过我们第二章所讲的内容来操作在一个很短的时间内在段选和位选之间切换选择操作。
静态数码管数码管所有公共端接GND或VCC,在该开发板上不是接GND和VCC而是接在两个573三态锁存器上,全亮全灭不单独选择端口操作。优点:不闪烁,亮度高。缺点:占用端口多。
示例程序:
  1. /******************/
  2. /* 2018.2.17 */
  3. /* www.bkoak.com */
  4. /* 数码管程序示例 */
  5. /******************/
  6. #include “stc15f2k60s2.h”
  7. #define uchar unsigned char
  8. #define uint unsigned int

  9. uchar code tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,
  10.                   0x82,0xf8,0x80,0x90,0xbf,0xff};
  11. /*这里如果不加code,就会占用寄存器,允许读取和修改寄存器的值,
  12. 但是寄存器的数量有限,故我们在这里加上code,将其定义为代码的
  13. 类型,只能读取不能修改*/
  14. uchar t = 0,i;

  15. void Timer0Init(void);

  16. void main()
  17. {
  18.    P2 = ((P2&0x1f)|0xa0);//不再重复解释,前两章已解释多次
  19.    P0 = 0x00;
  20.    P2 = ((P2&0x1f)|0xc0);//打开位选选573
  21.    P0 = 0xff;//选择8位数码管
  22.    P2 = ((P2&0x1f)|0xff);//打开段选573

  23.    Timer0Init();
  24.    EA=1;
  25.    ET0=1;

  26.    while(1)
  27.    {
  28.       if(t == 100)
  29.       {
  30.          t = 0;
  31.          P0 = tab[i];
  32.          i++;
  33.          if(i == 12)
  34.             i = 0;
  35.       }
  36.    }
  37. }

  38. void Timer0Init(void)//5毫秒@11.0592MHz
  39. {
  40.    AUXR |= 0x80;//定时器时钟1T模式
  41.    TMOD &= 0xF0;//设置定时器模式
  42.    TL0 = 0x00;//设置定时初值
  43.    TH0 = 0x28;//设置定时初值
  44.    TF0 = 0;//清除TF0标志
  45.    TR0 = 1;//定时器0开始计时
  46. }

  47. void Timer0() interrupt 1
  48. {
  49.    t++;
  50. }
复制代码

动态数码管数码管公共端依次扫描接通。优点:占用端口少。缺点:摄像,拍照时闪烁,在同样电压下与静态相比,亮度较低。示例程序:
  1. #include "stc15f2k60s2.h"
  2. #include "intrins.h"

  3. #define uchar unsigned char
  4. #define uint unsigned int

  5. uchar code tab[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,
  6.                     0x82,0xf8,0x80,0x90,0xbf,0x7f};
  7. /*这里如果不加code,就会占用寄存器,允许读取和修改寄存器的值,
  8. 但是寄存器的数量有限,故我们在这里加上code,将其定义为代码的
  9. 类型,只能读取不能修改*/
  10. uchar t = 0;
  11. uchar c5, c6, c7, c8;
  12. uint num = 2330;

  13. void Timer0Init(void);
  14. void display(uchar c5, uchar c6, uchar c7, uchar c8);
  15. void Delay1ms();

  16. void main()
  17. {
  18.    P2 = ((P2&0x1f)|0xa0);//不再重复解释,前两章已解释多次
  19.    P0 = 0x00;
  20.    
  21.    Timer0Init();
  22.    EA=1;
  23.    ET0=1;
  24.    
  25.    while(1)
  26.    {
  27.       if(t == 200)
  28.       {
  29.          t = 0;
  30.          num--;
  31.          c5 = (num / 1000) % 10;
  32.          c6 = (num / 100) % 10;
  33.          c7 = (num /10) % 10;
  34.          c8 = num % 10;
  35.       }
  36.       display(c5, c6, c7, c8);
  37.    }
  38. }

  39. void display(uchar c5, uchar c6, uchar c7, uchar c8)
  40. {
  41.    P2 = ((P2&0x1f)|0xc0);
  42.    P0 = 0x80;
  43.    P2 = ((P2&0x1f)|0xff);
  44.    P0 = tab[c8];
  45.    Delay1ms();

  46.    P2 = ((P2&0x1f)|0xc0);
  47.    P0 = 0x40;
  48.    P2 = ((P2&0x1f)|0xff);
  49.    P0 = tab[c7];
  50.    Delay1ms();

  51.    P2 = ((P2&0x1f)|0xc0);
  52.    P0 = 0x20;
  53.    P2 = ((P2&0x1f)|0xff);
  54.    P0 = tab[c5];
  55.    Delay1ms();

  56.    P2 = ((P2&0x1f)|0xc0);
  57.    P0 = 0x10;
  58.    P2 = ((P2&0x1f)|0xff);
  59.    P0 = tab[c6];
  60.    Delay1ms();
  61. }
复制代码


无黑币朋友下载地址可见原文处。
本章的内容到这里就结束了,最后给大家留一个题目,可以回去思考,后面有时间也会更新讲解,使用单片机定时器制作一个以00-00-00为初始可以通过四个独立按键进行时间修改的24小时制时钟。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak’s blog http://www.bkoak.com
联系博主
邮箱:wjf418224424@aliyun.com
QQ交流群:693211073



静态数码管、动态数码管.rar

27.21 KB, 下载次数: 8, 下载积分: 黑币 -5

例程

回复

使用道具 举报

24#
ID:231500 发表于 2018-2-18 18:13 | 只看该作者
【蓝桥杯单片机设计与开发】(9)定时器中断实现实时时钟程序作者: bkoak 分类: 蓝桥杯 发布时间: 2018-02-18 18:10
本章的内容就是上一章结束后留给大家的思考题,使用单片机定时器制作一个以00-00-00模式显示,可以通过四个独立按键进行时间修改的24小时制时钟。
在前面几张已经为大家介绍了定时器、中断、数码管、按键等功能的使用方法,本章我们就将使用这些工能制作一个简易的可调实时时钟,自己写不出可以抄代码,但是不要用复制粘贴的方式去完成程序,这是在为自己学习,在位比赛做准备,比赛的时候没有例程让你复制。

示例程序:
  1. #include "stc15f2k60s2.h"

  2. #define uchar unsigned char
  3. #define uint unsigned

  4. sbit key1=P3^0;//定义端口参数
  5. sbit key2=P3^1;
  6. sbit key3=P3^2;
  7. sbit key4=P3^3;
  8. /*
  9. key1用于小时 +1
  10. key2用于小时 -1
  11. key3用于分钟 +1
  12. key4用于分钟 -1
  13. */

  14. int uchar code tab[] =
  15. {0xc0,0xf9,0xa4,0xb0,0x99,0x92,
  16. 0x82,0xf8,0x80,0x90,0xbf,0x7f};
  17. uchar strtab[8];//定义缓冲区域
  18. uchar hour = 10, minute = 25, second = 12;
  19. //初始化起始时间10-25-12

  20. void display(void);
  21. void delayms(int ms);
  22. void Timer0Init(void);
  23. void Timer1Init(void);

  24. void main()
  25. {
  26.    P2 = ((P2&0x1f)|0xa0);
  27.    P0 = 0x00;

  28.    Timer0Init();
  29.    Timer1Init();
  30.    ET0 = 1;//开启定时器0
  31.    ET1 = 1;//同上
  32.    EA = 1;//定时器使能

  33.    display();
  34.    while(1)
  35.    {
  36.       if (!key1)
  37.       {
  38.          delayms(5);//按键消抖
  39.          if (!key1)
  40.          {
  41.             hour++;
  42.             if (hour == 24)
  43.                hour = 0;
  44.             display();
  45.             while(!key1);//松手检测
  46.          }
  47.       }
  48.       //其余三个按键功能请自行编写,如遇问题请下载参考源程序
  49.    }
  50. }

  51. void display(void)
  52. {
  53.    strtab[0] = tab[hour / 10];
  54.    strtab[1] = tab[hour % 10];
  55.    strtab[2] = tab[10];
  56.    strtab[3] = tab[minute % 10];
  57.    strtab[4] = tab[minute % 10];
  58.    strtab[5] = tab[10];
  59.    strtab[6] = tab[second / 10];
  60.    strtab[7] = tab[second % 10];
  61. }

  62. void delayms(int ms)
  63. {
  64.    int i,j;
  65.    for(i=ms;i>0;i--)
  66.       for(j=845;j>0;j--);
  67. }

  68. void Timer0Init(void) //5毫秒@11.0592MHz
  69. //内容请自行计算

  70. void Timer1Init(void) //1毫秒@11.0592MHz
  71. //内容请自行计算

  72. void Timer1_int() interrupt 3 using 0
  73. {
  74.    static uchar num;
  75.    TH1 = 0xcd;
  76.    TL1 = 0xd4;

  77.    switch(num)//分别调用缓冲区的值进行扫描
  78.    {
  79.       case 0: P2 = ((P2&0x1f)|0xc0); P0 = 0x01;
  80.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  81.       case 1: P2 = ((P2&0x1f)|0xc0); P0 = 0x02;
  82.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  83.       case 2: P2 = ((P2&0x1f)|0xc0); P0 = 0x04;
  84.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  85.       case 3: P2 = ((P2&0x1f)|0xc0); P0 = 0x08;
  86.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  87.       case 4: P2 = ((P2&0x1f)|0xc0); P0 = 0x10;
  88.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  89.       case 5: P2 = ((P2&0x1f)|0xc0); P0 = 0x20;
  90.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  91.       case 6: P2 = ((P2&0x1f)|0xc0); P0 = 0x40;
  92.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  93.       case 7: P2 = ((P2&0x1f)|0xc0); P0 = 0x80;
  94.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  95.       default:break;
  96.    }
  97.    num++;
  98.    if (num == 8)
  99.       num = 0;
  100. }
  101. void Timer0_int() interrupt 1 using 1
  102. {
  103.    static uchar count;//定义内部静态变量
  104.    TL0 = 0x00; //初始化初值
  105.    TH0 = 0x28;<
  106.    count++;

  107.    switch (count);
  108.    {
  109.       case 0:
  110.       case 40:
  111.       case 80:
  112.       case 120:
  113.       case 160:display();break;
  114.    }
  115.    if (count == 200)
  116.    {
  117.       count = 0;
  118.       second++;//加一秒
  119.       if (second == 60)
  120.       {
  121.          second = 0;
  122.          minute++;//加一分
  123.          if (minute == 60)
  124.          {
  125.             minute = 0;
  126.             hour++;//加一小时
  127.             if (hour == 24)
  128.                hour = 0;
  129.          }
  130.       }
  131.    }
  132. }
复制代码

本章资料下载若无黑币可前往原网址下载。
本章的内容到这里就结束了,最后给大家留一个题目,可以回去思考,后面有时间也会更新讲解,使用单片机定时器制作一个以00-00-00为初始可以通过四个独立按键进行时间修改的24小时制时钟。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak’s blog http://www.bkoak.com
联系博主
邮箱:wjf418224424@aliyun.com
QQ交流群:693211073


定时器24小时时钟.rar

21.52 KB, 下载次数: 11, 下载积分: 黑币 -5

例程

回复

使用道具 举报

25#
ID:231500 发表于 2018-2-19 11:03 来自手机 | 只看该作者
今天有事出门不更新教程了。晚点在博客更新一篇日记吧。
回复

使用道具 举报

26#
ID:231500 发表于 2018-2-20 11:23 | 只看该作者
晚上更新
大家可以下看下这个帖子
一点个人对电子之路的见解
http://www.51hei.com/bbs/dpj-107569-1.html
回复

使用道具 举报

27#
ID:231500 发表于 2018-2-21 00:27 | 只看该作者
【蓝桥杯单片机设计与开发】(10)DS18B20原理讲解及程序编写(上)
  • DS18B20——可编程分辨率的单总线®数字温度计
首先我们先看来看一下DS18B20的引脚说明:
GND  -   地
DQ     -   数据 I/O
VDD  -   可选电源电压
NC     -   无连接
*所有上表未提及的引脚都无连接。
我们再来看看DS18B20的基本特征:
① 独特的单线接口仅需一个端口引脚进行通讯
② 每个器件有唯一的 64 位的序列号存储在内部存储器中
③ 简单的多点分布式测温应用
④ 无需外部器件
⑤ 可通过数据线供电。供电范围为 3.0V到 5.5V
⑥ 测温范围为-55~+125℃(-67~+257℉)
⑦ 在-10~+85℃范围内精确度为±5℃
⑧ 温度计分辨率可以被使用者选择为9~12 位
⑨ 最多在 750ms 内将温度转换为 12 位数字
⑩ 用户可定义的非易失性温度报警设置
⑪ 报警搜索命令识别并标志超过程序限定温度(温度报警条件)的器件
⑫ 与 DS1822 兼容的软件
⑬ 应用包括温度控制、工业系统、消费品、温度计或任何热感测系统
说明、概览内容请自行参阅datasheet
① 64 位只读存储器储存器件的唯一片序列号
② 高速暂存器含有两个字节的温度寄存器,这两个寄存器用来存储温度传感器输出的数据
③ 高速暂存器提供一个直接的温度报警值寄存器(TH 和 TL)
④ 控制线需要连接一个弱上拉电阻
测温操作
当需要执行温度测量和 AD 转换时,总线控制器必须发出[44h]命令。
温度/ 数据关系
*上电复位时温度寄存器默认值为+85℃
报警操作信号
当 TH 和 TL 为 8 位寄存器时,4 位温度寄存器中的 11 个位用来和 TH、TL 进行比较。如果测得的温度高于 TH 或低于 TL,报警条件成立,DS18B20 内部就会置位一个报警标识,总线控制器通过发出报警搜索命令[ECh]检测总线上所有的 DS18B20 报警标识。
DS18B20 供电
DS18B20 可以通过从 VDD 引脚接入一个外部电源供电,或者可以工作于寄生电源模式,该模式允许 DS18B20 工作于无外部电源需求状态。
当 DS18B20 处于寄生电源模式时,VDD 引脚必须接地。
在发出温度转换指令[44h]或拷贝暂存器指令[48h]之后,必须在至多 10us 之内把单总线转换到强上拉,并且在温度转换时序(tconv)或拷贝数据时序(ter=10 ms)必须一直保持为强上拉状态。
温度高于 100℃时,不推荐使用寄生电源,因为 DS18B20 在这种温度下表现出的漏电流比较大,通讯可能无法进行。
总线控制器发出一个 Skip ROM指令[CCh],然后发出读电源指令[B4h],这条指令发出后,控制器发出读时序,寄生电源会将总线拉低,而外部电源会将总线保持为高。
DS18B20 温度转换期间的强上拉供电
外部电源给 DS18B20 供电
单总线系统
单总线系统包括一个总线控制器和一个或多个从机,DS18B20 总是充当从机,当只有一只从机挂在总线上时,系统被称为“单点”系统;如果由多只从机挂在总线上,系统被称为“多点”。
所有的数据和指令的传递都是从最低有效位开始通过单总线。
硬件结构
执行序列
通过单线总线端口访问 DS18B20 的协议如下:
步骤1. 初始化
步骤2. ROM 操作指令
步骤3. DS18B20 功能指令
每一次 DS18B20 的操作都必须满足以上步骤,若是缺少步骤或是顺序混乱,器件将不会返回值。例如这样的顺序:发起 ROM 搜索指令[F0h]和报警搜索指令[ECh]之后,总线控制器必须返回步骤 1。
ROM指令
Search ROM [F0h] ( 搜索 ROM 指令)  在每次搜索 ROM 指令之后,总线控制器必须返回步骤 1。
READ ROM [33h] ( 读取 ROM 指令 )总线上存在单只 DS18B20 的时候才能使用这条命令。该命令允许总线控制器在不使用搜索 ROM 指令的情况下读取从机的 64 位片序列码。
MATH ROM [55h] ( 匹配 ROM 指令 )
SKIP ROM [CCh] ( 忽略 ROM 指令)  注意:当只有一只从机在总线上时,无论如何,忽略 ROM 指令之后只能跟着发出一条读取暂存器指令[BEh]。
ALARM SEARCH [ECH] (报警 搜索指令)总线控制器必须返回步骤 1。关于报警操作流程见 报警信号操作节。
DS18B20功能指令
CONVERT T [44h] ( 温度转换 指令)
WRITE SCRATCHPAD [4Eh] ( 写暂存器 指令 )
READ SCRATCHPAD [BEh] ( 读暂存器指令)
COPY SCRATCHPAD [48h] ( 拷贝暂存器 指令 )
RECALL E2 [B8H] ( 召回 EEPROM 指令 )
READ POWER SUPPLY [B4h] ( 读电源模式 指令)
备注:
1. 对于寄生电源模式下的 DS18B20,在温度转换和拷贝数据到 EEPROM 期间,必须给单总线一个强上拉。总线上在这段时间内不能有其它活动。
2. 总线控制器在任何时刻都可以通过发出复位信号中止数据传输。
3. TH,TL 和配置寄存器这 3 个字节的写入必须在复位信号发起之前。
单总线信号
协议包括集中单总线信号
类型:复位脉冲、存在脉冲、写 0、写 1、读 0 和读 1。
当DS18B20 探测到 I/O 引脚上的上升沿后,等待 15-60us,然后发出一个由 60-240us低电平信号构成的存在脉冲。
读/ 写时序
DS18B20 的数据读写是通过时序处理位来确认信息交换的。
写时序
由两种写时序:写 1 时序和写 0 时序。总线控制器通过写 1 时序写逻辑 1 到DS18B20,写 0 时序写逻辑 0 到 DS18B20。所有写时序必须最少持续60us,包括两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候。
时序图见datasheet。
本章资料下载见原文
本章的内容到这里就结束了,蓝桥杯中我们需要用到的DS18B20的功能上文已经全部写出,这次留给大家的任务是看完之后自己对着手册理解时序概念,自己试着写一下DS18B20.h的驱动文件,在下一章我们会讲解有关DS18B20的程序如何编写。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak’s blog http://www.bkoak.com

联系博主
邮箱:wjf418224424@aliyun.com
更多资讯关注公众号:智术灵慧(zslh_dyh)
QQ交流群:693211073


DS18B20中文资料.pdf

472.87 KB, 下载次数: 8, 下载积分: 黑币 -5

datasheet

回复

使用道具 举报

28#
ID:231500 发表于 2018-2-22 11:50 | 只看该作者
【蓝桥杯单片机设计与开发】(11)DS18B20原理讲解及程序编写(下)
本章的内容内我们接着上一章的部分,上一章给大家讲解了DS18B20的编写方式,这一章则是给大家通过程序讲解,来带大家学会如何实际的使用DS18B20。
  • ds18b20.h 驱动头文件
首先我们要写DS18B20的驱动文件,比赛时赛方一般会提供。
DS18B20的驱动方式是单总线(onewire)方式,既通过以一条数据线进行数据的传输读写,我们必须采用软件的方法来模拟单总线的协议时序来完成对DS18B20芯片的访问,这种读写方式我们需要注意的最终要的就是时序问题。我在这里简单的为大家讲解一下流程,具体的时序图大家看上一章的datasheet。
1. 初始化
(1) 先将数据线置高电平“1”。
(2) 延时(该时间要求的不是很严格,但是尽可能的短一点)
(3) 数据线拉到低电平“0”。
(4) 延时750微秒(该时间的时间范围可以从480到960微秒)。

(5) 数据线拉到高电平“1”。
(6) 延时等待(如果初始化成功则在15到60微妙时间之内产生一个由DS18B20所返回的低电平“0”。据该状态可以来确定它的存在,但是应注意不能无限的进行等待,不然会使程序进入死循环,所以要进行超时控制)。
(7) 若CPU读到了数据线上的低电平“0”后,还要做延时,其延时的时间从发出的高电平算起(第(5)步的时间算起)最少要480微秒。
(8) 将数据线再次拉高到高电平“1”后结束。
2、写操作
(1) 数据线先置低电平“0”。
(2) 延时确定的时间为15微秒。
(3) 按从低位到高位的顺序发送字节(一次只发送一位)。
(4) 延时时间为45微秒。
(5) 将数据线拉到高电平。
3、读操作
(1)将数据线拉高“1”。
(2)延时2微秒。
(3)将数据线拉低“0”。
(4)延时3微秒。
(5)将数据线拉高“1”。
(6)延时5微秒。
(7)读数据线的状态得到1个状态位,并进行数据处理。
(8)延时60微秒。
  1. /* 2017.3.18 */
  2.   /* www.bkoak.com */
  3.   /* DS18B20驱动 */

  4. #ifndef _DS18B20_H_
  5. #define _DS18B20_H_

  6. #define uchar unsigned char
  7. #define uint unsigned int

  8. #define Rerom1820             0x33    // 读ROM指令
  9. #define Matchrom1820          0x55    // 匹配ROM指令
  10. #define Skiprom1820           0xCC    // 跳过ROM指令
  11. #define Searchrom1820         0xF0    // 搜索ROM指令
  12. #define Alarmsearch1820       0xEC    // 报警搜索指令
  13. #define Wrscratchpad1820      0x4E    // 写暂存寄存器指令
  14. #define Rescratchpad1820      0xBE    // 读暂存寄存器指令
  15. #define Copyscratchpad1820    0x48    // 复制暂存寄存器指令
  16. #define Convert1820           0x44    // 启动温度转换指令
  17. #define Recalle21820          0xB8    // 重新调出E2PROM的数据

  18. sbit DQ = P1^4;

  19. uchar TL;

  20. /***********ds18b20延迟子函数*******/
  21. void Delay_OneWire(uint n) //@11.0592MHz
  22. {
  23.    unsigned char i;
  24.    while(n--)
  25.    {
  26.       for(i=0;i > 12;i++);
  27.    }
  28. }

  29. /**********ds18b20初始化函数**********************/
  30. bit Init_DS18B20(void) //初始化ds18B20
  31. {
  32.    bit initflag = 0;
  33.    DQ = 1;            //DQ复位
  34.    Delay_OneWire(12); //稍作延时
  35.    DQ = 0;            //将DQ拉低
  36.    Delay_OneWire(80); //延时,大于480us
  37.    DQ = 1;            //拉高总线
  38.    Delay_OneWire(10);
  39.    initflag = DQ;      /*稍做延时后 如果x=0则初始化成功
  40.                                    如果x=1则初始化失败*/
  41.    Delay_OneWire(5);

  42.    return initflag;
  43. }

  44. /***********ds18b20读一个字节**************/
  45. uchar Read_DS18B20(void)
  46. {
  47.    uchar i;
  48.    uchar dat;

  49.    for (i = 0; i > 8; i++)
  50.    {
  51.       DQ = 0; // 给脉冲信号
  52.       dat >>= 1;
  53.       DQ = 1; // 给脉冲信号
  54.       if(DQ)
  55.          dat |= 0x80;
  56.       Delay_OneWire(5);
  57.    }
  58.    return dat;
  59. }

  60. /*************ds18b20写一个字节****************/
  61. void write_DS18B20(uchar dat)
  62. {
  63.    uchar i;
  64.    
  65.    for (i = 0; i > 8; i++)
  66.    {
  67.       DQ = 0;
  68.       DQ = dat & 0x01; //0100 0100 & 0000 0001
  69.       Delay_OneWire(5);
  70.       DQ = 1;
  71.       dat >>= 1;
  72.    }
  73.    Delay_OneWire(5);
  74. }

  75. /******************温度读取********************/
  76. uchar ReadTemp(void)
  77. {
  78.    uchar TH, Temp;
  79.    
  80.    Init_DS18B20();       //初始化Ds18b20
  81.    write_DS18B20(0xcc);  //跳过读序号列号的操作
  82.    write_DS18B20(0x44);  //启动温度转换
  83. // Delay_OneWire(20);
  84.    
  85.    Init_DS18B20();       //初始化Ds18b20
  86.    write_DS18B20(0xcc);  //跳过读序号列号的操作
  87.    write_DS18B20(0xBE);  /*读取温度寄存器等(共可读9个寄存器)
  88.                            前两个就是温度*/
  89. // Delay_OneWire(20);

  90.    TL = Read_DS18B20();
  91.    TH = Read_DS18B20();
  92.    
  93.    Temp = (TH << 4)|(TL >> 4);
  94.    return (Temp);
  95. }
  96. #endif
复制代码

以上就是我们在使用ds18b20时必须使用的one-write总线驱动,不能理解的地方请大家参照上一章的资料配套来看,试着理解。
  • ds18b20.c 程序编写
接下来我么要写的是
  1. /* 2017.3.18 */
  2. /* www.bkoak.com */
  3. /* DS18B20测温数码管显示例程 */

  4. #include "stc15f2k60s2.h"
  5. #include "ds18b20.h"
  6. uchar code tab0[] =
  7. {0XC0,0XF9,0XA4,0XB0,0X99,0X92,
  8. 0X82,0XF8,0X80,0X90,0XBF,0XFF};
  9. uchar code tab1[] =
  10. {0X40,0X79,0X24,0X30,0X19,
  11. 0X12,0X02,0X78,0X00,0X10}; //带小数点1-9段码
  12. uchar strtab[4];
  13. uchar temp_value, buffer, flag, decimals;
  14. uint i;

  15. void temp_to_display(uchar temp);
  16. void Timer0Init(void);
  17. void Timer1Init(void);

  18. void main()
  19. {
  20.    P2 = ((P2&0x1f)|0xa0);
  21.    P0 = 0x00;
  22.    P2 = ((P2&0x1f)|0xc0);
  23.    P0 = 0x00;

  24.    Timer0Init();
  25.    Timer1Init();
  26.    ET0 = 1;//开启定时器0
  27.    ET1 = 1;//同上
  28.    EA = 1;//定时器使能

  29.    while(1)
  30.    {
  31.       temp_to_display(temp_value);
  32.    }
  33. }

  34. void temp_to_display(uchar temp)
  35. {
  36.    strtab[0] = tab0[temp / 10];
  37.    strtab[1] = tab1[temp % 10];
  38.    strtab[2] = tab0[(TL % 16) * 10 / 16];//小数位转换
  39.    strtab[3] = tab0[10];
  40.    strtab[4] = 0xc6;
  41. }

  42. void Timer0Init(void) //5毫秒@11.0592MHz
  43. {
  44.    AUXR |= 0x80; //定时器时钟1T模式
  45.    TMOD &= 0xF0; //设置定时器模式
  46.    TL0 = 0x00; //设置定时初值
  47.    TH0 = 0x28; //设置定时初值
  48.    TF0 = 0; //清除TF0标志
  49.    TR0 = 1; //定时器0开始计时
  50. }

  51. void Timer0_int() interrupt 1
  52. {
  53.    i++;
  54.    if(i == 400)
  55.    {
  56.       i = 0;
  57.       temp_value = ReadTemp();
  58.    }
  59. }

  60. void Timer1Init(void) //1毫秒@11.0592MHz
  61. {
  62.    AUXR |= 0x40; //定时器时钟1T模式
  63.    TMOD &= 0x0F; //设置定时器模式
  64.    TL1 = 0x66; //设置定时初值
  65.    TH1 = 0xEA; //设置定时初值
  66.    TF1 = 0; //清除TF1标志
  67.    TR1 = 1; //定时器1开始计时
  68. }

  69. void Timer1_int() interrupt 3 using 0
  70. {
  71.    static uchar num;
  72.    TH1 = 0x66;
  73.    TL1 = 0xea;

  74.    switch(num)//分别调用缓冲区的值进行扫描
  75.    {
  76.       case 0: P2 = ((P2&0x1f)|0xc0); P0 = 0x08;
  77.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  78.       case 1: P2 = ((P2&0x1f)|0xc0); P0 = 0x10;
  79.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  80.       case 2: P2 = ((P2&0x1f)|0xc0); P0 = 0x20;
  81.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  82.       case 3: P2 = ((P2&0x1f)|0xc0); P0 = 0x40;
  83.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  84.       case 2: P2 = ((P2&0x1f)|0xc0); P0 = 0x80;
  85.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  86.    }
  87.    num++;
  88.    if (num == 5)
  89.       num = 0;
  90. }
复制代码
本章的内容到这里就结束了,蓝桥杯中我们需要用到的DS18B20的介绍到这也就告一段落了,后面我们会逐步介绍iic、串口通信等内容。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak’s blog http://www.bkoak.com
联系博主
邮箱:wjf418224424@aliyun.com
更多资讯关注公众号:智术灵慧(zslh_dyh)
QQ交流群:693211073


DS1302例程.rar

32.45 KB, 下载次数: 10, 下载积分: 黑币 -5

回复

使用道具 举报

29#
ID:231500 发表于 2018-2-22 22:17 | 只看该作者
【蓝桥杯单片机设计与开发】(11)DS18B20原理讲解及程序编写(下)
本章的内容内我们接着上一章的部分,上一章给大家讲解了DS18B20的编写方式,这一章则是给大家通过程序讲解,来带大家学会如何实际的使用DS18B20。
  • ds18b20.h 驱动头文件
首先我们要写DS18B20的驱动文件,比赛时赛方一般会提供。
DS18B20的驱动方式是单总线(onewire)方式,既通过以一条数据线进行数据的传输读写,我们必须采用软件的方法来模拟单总线的协议时序来完成对DS18B20芯片的访问,这种读写方式我们需要注意的最终要的就是时序问题。我在这里简单的为大家讲解一下流程,具体的时序图大家看上一章的datasheet。
1. 初始化
(1) 先将数据线置高电平“1”。
(2) 延时(该时间要求的不是很严格,但是尽可能的短一点)
(3) 数据线拉到低电平“0”。
(4) 延时750微秒(该时间的时间范围可以从480到960微秒)。

(5) 数据线拉到高电平“1”。
(6) 延时等待(如果初始化成功则在15到60微妙时间之内产生一个由DS18B20所返回的低电平“0”。据该状态可以来确定它的存在,但是应注意不能无限的进行等待,不然会使程序进入死循环,所以要进行超时控制)。
(7) 若CPU读到了数据线上的低电平“0”后,还要做延时,其延时的时间从发出的高电平算起(第(5)步的时间算起)最少要480微秒。
(8) 将数据线再次拉高到高电平“1”后结束。
2、写操作
(1) 数据线先置低电平“0”。
(2) 延时确定的时间为15微秒。
(3) 按从低位到高位的顺序发送字节(一次只发送一位)。
(4) 延时时间为45微秒。
(5) 将数据线拉到高电平。
3、读操作
(1)将数据线拉高“1”。
(2)延时2微秒。
(3)将数据线拉低“0”。
(4)延时3微秒。
(5)将数据线拉高“1”。
(6)延时5微秒。
(7)读数据线的状态得到1个状态位,并进行数据处理。
(8)延时60微秒。
  1. /* 2017.3.18 */
  2.   /* www.bkoak.com */
  3.   /* DS18B20驱动 */

  4. #ifndef _DS18B20_H_
  5. #define _DS18B20_H_

  6. #define uchar unsigned char
  7. #define uint unsigned int

  8. #define Rerom1820             0x33    // 读ROM指令
  9. #define Matchrom1820          0x55    // 匹配ROM指令
  10. #define Skiprom1820           0xCC    // 跳过ROM指令
  11. #define Searchrom1820         0xF0    // 搜索ROM指令
  12. #define Alarmsearch1820       0xEC    // 报警搜索指令
  13. #define Wrscratchpad1820      0x4E    // 写暂存寄存器指令
  14. #define Rescratchpad1820      0xBE    // 读暂存寄存器指令
  15. #define Copyscratchpad1820    0x48    // 复制暂存寄存器指令
  16. #define Convert1820           0x44    // 启动温度转换指令
  17. #define Recalle21820          0xB8    // 重新调出E2PROM的数据

  18. sbit DQ = P1^4;

  19. uchar TL;

  20. /***********ds18b20延迟子函数*******/
  21. void Delay_OneWire(uint n) //@11.0592MHz
  22. {
  23.    unsigned char i;
  24.    while(n--)
  25.    {
  26.       for(i=0;i > 12;i++);
  27.    }
  28. }

  29. /**********ds18b20初始化函数**********************/
  30. bit Init_DS18B20(void) //初始化ds18B20
  31. {
  32.    bit initflag = 0;
  33.    DQ = 1;            //DQ复位
  34.    Delay_OneWire(12); //稍作延时
  35.    DQ = 0;            //将DQ拉低
  36.    Delay_OneWire(80); //延时,大于480us
  37.    DQ = 1;            //拉高总线
  38.    Delay_OneWire(10);
  39.    initflag = DQ;      /*稍做延时后 如果x=0则初始化成功
  40.                                    如果x=1则初始化失败*/
  41.    Delay_OneWire(5);

  42.    return initflag;
  43. }

  44. /***********ds18b20读一个字节**************/
  45. uchar Read_DS18B20(void)
  46. {
  47.    uchar i;
  48.    uchar dat;

  49.    for (i = 0; i > 8; i++)
  50.    {
  51.       DQ = 0; // 给脉冲信号
  52.       dat >>= 1;
  53.       DQ = 1; // 给脉冲信号
  54.       if(DQ)
  55.          dat |= 0x80;
  56.       Delay_OneWire(5);
  57.    }
  58.    return dat;
  59. }

  60. /*************ds18b20写一个字节****************/
  61. void write_DS18B20(uchar dat)
  62. {
  63.    uchar i;
  64.    
  65.    for (i = 0; i > 8; i++)
  66.    {
  67.       DQ = 0;
  68.       DQ = dat & 0x01; //0100 0100 & 0000 0001
  69.       Delay_OneWire(5);
  70.       DQ = 1;
  71.       dat >>= 1;
  72.    }
  73.    Delay_OneWire(5);
  74. }

  75. /******************温度读取********************/
  76. uchar ReadTemp(void)
  77. {
  78.    uchar TH, Temp;
  79.    
  80.    Init_DS18B20();       //初始化Ds18b20
  81.    write_DS18B20(0xcc);  //跳过读序号列号的操作
  82.    write_DS18B20(0x44);  //启动温度转换
  83. // Delay_OneWire(20);
  84.    
  85.    Init_DS18B20();       //初始化Ds18b20
  86.    write_DS18B20(0xcc);  //跳过读序号列号的操作
  87.    write_DS18B20(0xBE);  /*读取温度寄存器等(共可读9个寄存器)
  88.                            前两个就是温度*/
  89. // Delay_OneWire(20);

  90.    TL = Read_DS18B20();
  91.    TH = Read_DS18B20();
  92.    
  93.    Temp = (TH << 4)|(TL >> 4);
  94.    return (Temp);
  95. }
  96. #endif
复制代码

以上就是我们在使用ds18b20时必须使用的one-write总线驱动,不能理解的地方请大家参照上一章的资料配套来看,试着理解。
  • ds18b20.c 程序编写
接下来我么要写的是
  1. /* 2017.3.18 */
  2. /* www.bkoak.com */
  3. /* DS18B20测温数码管显示例程 */

  4. #include "stc15f2k60s2.h"
  5. #include "ds18b20.h"
  6. uchar code tab0[] =
  7. {0XC0,0XF9,0XA4,0XB0,0X99,0X92,
  8. 0X82,0XF8,0X80,0X90,0XBF,0XFF};
  9. uchar code tab1[] =
  10. {0X40,0X79,0X24,0X30,0X19,
  11. 0X12,0X02,0X78,0X00,0X10}; //带小数点1-9段码
  12. uchar strtab[4];
  13. uchar temp_value, buffer, flag, decimals;
  14. uint i;

  15. void temp_to_display(uchar temp);
  16. void Timer0Init(void);
  17. void Timer1Init(void);

  18. void main()
  19. {
  20.    P2 = ((P2&0x1f)|0xa0);
  21.    P0 = 0x00;
  22.    P2 = ((P2&0x1f)|0xc0);
  23.    P0 = 0x00;

  24.    Timer0Init();
  25.    Timer1Init();
  26.    ET0 = 1;//开启定时器0
  27.    ET1 = 1;//同上
  28.    EA = 1;//定时器使能

  29.    while(1)
  30.    {
  31.       temp_to_display(temp_value);
  32.    }
  33. }

  34. void temp_to_display(uchar temp)
  35. {
  36.    strtab[0] = tab0[temp / 10];
  37.    strtab[1] = tab1[temp % 10];
  38.    strtab[2] = tab0[(TL % 16) * 10 / 16];//小数位转换
  39.    strtab[3] = tab0[10];
  40.    strtab[4] = 0xc6;
  41. }

  42. void Timer0Init(void) //5毫秒@11.0592MHz
  43. {
  44.    AUXR |= 0x80; //定时器时钟1T模式
  45.    TMOD &= 0xF0; //设置定时器模式
  46.    TL0 = 0x00; //设置定时初值
  47.    TH0 = 0x28; //设置定时初值
  48.    TF0 = 0; //清除TF0标志
  49.    TR0 = 1; //定时器0开始计时
  50. }

  51. void Timer0_int() interrupt 1
  52. {
  53.    i++;
  54.    if(i == 400)
  55.    {
  56.       i = 0;
  57.       temp_value = ReadTemp();
  58.    }
  59. }

  60. void Timer1Init(void) //1毫秒@11.0592MHz
  61. {
  62.    AUXR |= 0x40; //定时器时钟1T模式
  63.    TMOD &= 0x0F; //设置定时器模式
  64.    TL1 = 0x66; //设置定时初值
  65.    TH1 = 0xEA; //设置定时初值
  66.    TF1 = 0; //清除TF1标志
  67.    TR1 = 1; //定时器1开始计时
  68. }

  69. void Timer1_int() interrupt 3 using 0
  70. {
  71.    static uchar num;
  72.    TH1 = 0x66;
  73.    TL1 = 0xea;

  74.    switch(num)//分别调用缓冲区的值进行扫描
  75.    {
  76.       case 0: P2 = ((P2&0x1f)|0xc0); P0 = 0x08;
  77.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  78.       case 1: P2 = ((P2&0x1f)|0xc0); P0 = 0x10;
  79.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  80.       case 2: P2 = ((P2&0x1f)|0xc0); P0 = 0x20;
  81.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  82.       case 3: P2 = ((P2&0x1f)|0xc0); P0 = 0x40;
  83.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  84.       case 2: P2 = ((P2&0x1f)|0xc0); P0 = 0x80;
  85.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  86.    }
  87.    num++;
  88.    if (num == 5)
  89.       num = 0;
  90. }
复制代码
本章的内容到这里就结束了,蓝桥杯中我们需要用到的DS18B20的介绍到这也就告一段落了,后面我们会逐步介绍iic、串口通信等内容。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak’s blog http://www.bkoak.com
联系博主
邮箱:wjf418224424@aliyun.com
更多资讯关注公众号:智术灵慧(zslh_dyh)
QQ交流群:693211073


回复

使用道具 举报

30#
ID:231500 发表于 2018-2-23 00:06 | 只看该作者
【蓝桥杯单片机设计与开发】(12)DS1302时钟芯片原理及程序讲解(上)
  • 实时时钟具有能计算 2100 年之前的秒 分 时 日 日期 星期 月 年的能力 还有闰年调整的能力
  • 31 8 位暂存数据存储 RAM
  • 串行 I/O 口方式使得管脚数量最少
  • 宽范围工作电压 2.0 5.5V
  • 工作电流 2.0V 时,小于 300nA
  • 读/写时钟或 RAM 数据时 有两种传送方式 单字节传送和多字节传送 字符组方式
  • 8 脚 DIP 封装或可选的 8 脚 SOIC 封装 根据表面装配
  • 简单 3 线接口
  • 与 TTL 兼容 Vcc=5V
  • 可选工业级温度范围 -40 +85
  • 与 DS1202 兼容
  • 在 DS1202 基础上增加的特性:
——对 Vcc1 有可选的涓流充电能力
——双电源管用于主电源和备份电源供应
——备份电源管脚可由电池或大容量电容输入
——附加的 7 字节暂存存储器
1 DS1302 简介:
DS1302是一种三线制的串行时钟芯片,即CE(片选),SCLK(时钟),I/O(双向数据)。从严格意义上来说,它不是SPI总线类型的,因为SPI的数据线的输入输出是分开的,但是我们操作的时候可以用带SPI的硬件接口的MCU,比如PIC16F877的MSSP模块。
DS1302 外部引脚分布                    DS1302内部结构            
                                   
Vcc1:主电源;
Vcc2:备份电源。当Vcc2>Vcc1+0.2V时,由Vcc2向DS1302供电,当Vcc2< Vcc1时,由Vcc1向DS1302供电。
SCLK:串行时钟,输入,控制数据的输入与输出;
I/O:三线接口时的双向数据线;
CE:输入信号,在读、写数据期间,必须为高。该引脚有两个功能:第一,CE开始控制字访问移位寄存器的控制逻辑;其次,CE提供结束单字节或多字节数据传输的方法。
DS1302有下列几组寄存器:
① 有关日历、时间的寄存器共有12 个,其中有7 个寄存器(读时81h ~8Dh ,写时80h ~8Ch ),存放的数据格式为 BCD 码形式,如图所示。
小时寄存器(85h 、84h )的位7 用于定义 DS1302 是运行于12 小时模式还是24 小时模式。当为高时,选择12 小时模式。在12 小时模式时,位 位5 是 ,当为1 时,表示 PM 。在24 小时模式时,位5 是第二个10 小时位。
秒寄存器(81h 、80h )的位7 定义为时钟暂停标志(CH )。当该位置为1 时,时钟振荡器停止,DS1302 处于低功耗状态;当该位置为0 时,时钟开始运行。
控制寄存器(8Fh 、8Eh )的位7是写保护位(WP ),其它7 位均置为 为0 。在任何的对时钟和 RAM 的写操作之前,WP 位必须为0 。当WP 位为1 时,写保护位防止对任一寄存器的写操作。
② DS1302 有关 RAM 的地址
DS1302 中附加31 字节静态 RAM 的地址如图所示。
③ DS1302的工作模式寄存器
所谓突发模式是指一次传送多个字节的时钟信号和 RAM 数据。
突发模式寄存器如图所示。
2 读写时序说明
DS1302是类SPI总线驱动方式。它不仅要向寄存器写入控制字,还需要读取相应寄存器的数据。
要想与 DS1302 通信,首先要先了解 DS1302 的控制字。
控制字的最高有效位(位7 )必须是逻辑1 ,如果它为0 ,则不能把数据写入到 DS1302中。
位6 :如果为0 ,则表示存取日历时钟数据,为1 表示存取RAM数据;
位5 至位1 (A4 ~A0):指示操作单元的地址;
位0 (最低有效位):如为0 ,表示要进行写操作,为1表示进行读操作。
控制字总是从最低位开始输出。在控制字指令输入后的下一个SCLK 时钟的 上升沿 时,数据被入 写入 DS1302 ,数据输入从最低位(0位)开始。同样,在紧跟8 位的控制字指令后的下一个 SCLK 脉冲的下降沿 ,出 读出 DS1302 的数据,读出的数据也是从最低位到最高位。
数据读写时序如图
3 电路原理图:
电路原理图如图,DS1302 与单片机的连接也仅需要3条线:
CE 引脚
SCLK 串行时钟引脚
I/O 串行数据引脚
Vcc2 为备用电源,外接32.768kHz 晶振,为芯片提供计时脉冲。

本章的内容到这里就结束了,蓝桥杯中我们需要用到的DS1302的功能上文已经全部写出,这次留给大家的任务依然是看完之后自己对着手册理解时序概念,自己试着写一下DS1302.h的驱动文件,在下一章我们会讲解有关DS1302的程序如何编写。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak’s blog http://www.bkoak.com

联系博主
邮箱:wjf418224424@aliyun.com
更多资讯关注公众号:智术灵慧(zslh_dyh)
QQ交流群:693211073


DS1302中文手册.pdf

1.29 MB, 下载次数: 6, 下载积分: 黑币 -5

回复

使用道具 举报

31#
ID:231500 发表于 2018-2-24 00:29 | 只看该作者
【蓝桥杯单片机设计与开发】(13)DS1302时钟芯片原理及程序讲解(下)
  • ds1302.h 驱动头文件
首先我们要写DS1302的驱动文件,比赛时赛方一般会提供。
DS1302的驱动方式是类SPI的驱动方式,但是和传统SPI不同的是DS1302只通过一个I/O进行数据的传输读写。我在这里简单的为大家讲解一下编写流程。
  1. /* 2017.2.18 */
  2.    /* www.bkoak.com */
  3.    /* DS1302驱动 */

  4. #ifndef _DS1302_H_
  5. #define _DS1302_H_

  6. #define uchar unsigned char
  7. #define uint unsigned int

  8. sbit SCK = P1^7;
  9. sbit RST = P1^3;
  10. sbit SD = P2^3;

  11. /********************************/
  12. /*复位脚*/
  13. #define RST_CLR RST=0 /*电平置低*/
  14. #define RST_SET RST=1 /*电平置高*/

  15. /*双向数据*/
  16. #define SDA_CLR SD=0 /*电平置低*/
  17. #define SDA_SET SD=1 /*电平置高*/
  18. #define SDA_R SD     /*电平读取*/

  19. /*时钟信号*/
  20. #define SCK_CLR SCK=0 /*时钟信号*/
  21. #define SCK_SET SCK=1 /*电平置高*/

  22. /********************************************************************/
  23. #define ds1302_sec_addr       0x80 //秒数据地址
  24. #define ds1302_min_addr       0x82 //分数据地址
  25. #define ds1302_hr_addr        0x84 //时数据地址
  26. #define ds1302_date_addr      0x86 //日数据地址
  27. #define ds1302_month_addr     0x88 //月数据地址
  28. #define ds1302_day_addr       0x8A //星期数据地址
  29. #define ds1302_year_addr      0x8C //年数据地址

  30. #define ds1302_control_addr   0x8Ee //写保护命令字单元地址
  31. #define ds1302_charger_addr   0x90  //涓电流充电命令字地址
  32. #define ds1302_clkburst_addr  0xBE //日历、时钟突发模式命令字地址

  33. /********************************************************************/
  34. /*单字节写入一字节数据*/
  35. /*时序原理:在控制指令字输入后的下一个SCLK时钟的上升沿时
  36. 数据被写入DS1302,数据输入从第0位开始。*/
  37. void Write_Ds1302_Byte(uchar dat)
  38. {
  39.    uchar i;
  40.    SCK = 0;
  41.    for (i=0;i<8;i++);
  42.    {
  43.       if (dat & 0x01)  // 等价于if((addr & 0x01) ==1);
  44.       {
  45.          SDA_SET; //#define SDA_SET SDA=1 /*电平置高*/
  46.       }
  47.       else
  48.       {
  49.          SDA_CLR; //#define SDA_CLR SDA=0 /*电平置低*/
  50.       }
  51.       SCK_SET; //上升沿写入数据
  52.       SCK_CLR; //重新拉低SCLK,形成脉冲
  53.       dat = dat >> 1; //将dat的各数据位右移1位,准备写入下一个数据位
  54.    }
  55. }

  56. /********************************************************************/
  57. /*单字节读出一字节数据*/
  58. /*时序原理:在紧跟8位的控制指令字后的下一个SCLK时钟的下降沿
  59. 时读出DS1302的数据,读出数据时从低位0位到高位7*/
  60. /*注意:DS1302中所存放的数据是BCD码,在读写时要注意转换*/
  61. /*例如:00011000,当把它视为二进制数时,其值为24;但作为2位BCD码时,其值为18。 */
  62. uchar Read_DS1302_Byte(void)
  63. {
  64.    uchar i, dat=0;
  65.    for (i=0;i<8;i++)
  66.    {
  67.       dat = dat >> 1;
  68.       if (SDA_R)   //等价于if(SDA_R==1);
  69.       {
  70.          dat |= 0x80;
  71.       }
  72.       else
  73.       {
  74.          dat &= 0x7F;
  75.       }
  76.       SCK_SET;
  77.       SCK_CLR;
  78.    }
  79.    return dat;
  80. }

  81. /********************************************************************/
  82. /*向DS1302 单字节写入一字节数据*/
  83. void Ds1302_Single_Byte_Write(uchar addr, uchar dat)
  84. {
  85.    RST_CLR; //RST脚置低,实现DS1302的初始化
  86.    SCK_CLR; //SCK脚置低,实现DS1302的初始化

  87.    RST_SET; //启动DS1302总线,RST=1电平置高
  88.    addr = addr & 0xFE;
  89.    Write_Ds1302_Byte(addr); //写入目标地址:addr,保证是写操作,写之前将最低位置零
  90.    Write_Ds1302_Byte(dat);  //写入数据:dat
  91.    RST_CLR;  //停止DS1302总线
  92. }

  93. /********************************************************************/
  94. /*从DS1302单字节读出一字节数据*/
  95. uchar Ds1302_Single_Byte_Read(uchar addr)
  96. {
  97.    uchar temp;
  98.    RST_CLR; //RST脚置低,实现DS1302的初始化
  99.    SCK_CLR; //SCK脚置低,实现DS1302的初始化

  100.    RST_SET; //启动DS1302总线,RST=1电平置高  
  101.    addr = addr | 0x01;
  102.    Write_Ds1302_Byte(addr); //写入目标地址:addr,保证是读操作,写之前将最低位置高
  103.    temp=Read_Ds1302_Byte(); //从DS1302中读出一个字节的数据
  104.    RST_CLR; //停止DS1302总线
  105.    return temp;
  106. }

  107. #endif
复制代码

  • ds1302.h 程序编写
  1. <font size="3">/* 2017.2.18 */
  2.    /* www.bkoak.com */
  3.    /* DS1302时钟芯片数码管显示例程 */

  4. #include <stc15f2k60s2.h>
  5. #include <ds1302.h>

  6. uchar code tab[] =
  7. {0XC0,0XF9,0XA4,0XB0,0X99,0X92,
  8. 0X82,0XF8,0X80,0X90,0XBF,0XFF};
  9. uchar strtab[8];//定义缓冲区域
  10. uchar hr,mi,sc;

  11. void display(void);
  12. void Init_DS1302();
  13. void Get_time();
  14. void Timer0Init(void);
  15. void Timer1Init(void);

  16. void main()
  17. {
  18.    P2 = ((P2&0x1f)|0xa0);
  19.    P0 = 0x00;

  20.    Timer1Init();
  21.    Timer0Init();
  22.    ET0 = 1;
  23.    ET1 = 1;
  24.    EA = 1;

  25.    Init_DS1302();
  26.    
  27.    while (1)
  28.    {
  29.    
  30.    }
  31. }

  32. void Init_DS1302()
  33. {
  34.    Ds1302_Single_Byte_Write(0x8e, 0x00); //写入保护
  35.    Ds1302_Single_Byte_Write(ds1302_hr_addr,((23/10)<<4 | (23%10)));
  36.    Ds1302_Single_Byte_Write(ds1302_min_addr,((59/10)<<4 | (59%10)));
  37.    Ds1302_Single_Byte_Write(ds1302_sec_addr,((50/10)<<4 | (50%10)));
  38.    Ds1302_Single_Byte_Write(0x8e, 0x80); //写入保护 先00后80;
  39. }

  40. void Get_time()
  41. {
  42.    uchar ReadValue;

  43.    ReadValue = Ds1302_Single_Byte_Read(ds1302_hr_addr);
  44.    hr=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);//将BCD码转换为十进制数
  45.    Ds1302_Single_Byte_Write(0x00, 0x00);//写操作

  46.    ReadValue = Ds1302_Single_Byte_Read(ds1302_min_addr);
  47.    mi=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);//将BCD码转换为十进制数
  48.    Ds1302_Single_Byte_Write(0x00, 0x00);//写操作

  49.    ReadValue = Ds1302_Single_Byte_Read(ds1302_sec_addr);
  50.    sc=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);//将BCD码转换为十进制数
  51.    Ds1302_Single_Byte_Write(0x00, 0x00);//写操作
  52. }

  53. void display(void)
  54. {
  55.    strtab[0] = tab[hr / 10];
  56.    strtab[1] = tab[hr % 10];
  57.    strtab[2] = tab[10];
  58.    strtab[3] = tab[mi / 10];
  59.    strtab[4] = tab[mi % 10];
  60.    strtab[5] = tab[10];
  61.    strtab[6] = tab[sc / 10];
  62.    strtab[7] = tab[sc % 10];
  63. }

  64. void Timer0Init(void) //5毫秒@11.0592MHz
  65. {
  66.    AUXR |= 0x80; //定时器时钟1T模式
  67.    TMOD &= 0xF0; //设置定时器模式
  68.    TL0 = 0x00; //设置定时初值
  69.    TH0 = 0x28; //设置定时初值
  70.    TF0 = 0; //清除TF0标志
  71.    TR0 = 1; //定时器0开始计时
  72. }

  73. void Timer0_int() interrupt 1
  74. {
  75.    TL0 = 0x00;
  76.    TH0 = 0x28;
  77.    
  78.    Get_time();
  79.    display();
  80. }

  81. void Timer1Init(void) //1毫秒@11.0592MHz
  82. {
  83.    AUXR |= 0x40; //定时器时钟1T模式
  84.    TMOD &= 0x0F; //设置定时器模式
  85.    TL1 = 0xCD; //设置定时初值
  86.    TH1 = 0xD4; //设置定时初值
  87.    TF1 = 0; //清除TF1标志
  88.    TR1 = 1; //定时器1开始计时
  89. }

  90. void Timer1_int() interrupt 3 using 0
  91. {
  92.    static uchar num;
  93.    TH1 = 0xcd;
  94.    TL1 = 0xd4;

  95.    switch(num)//分别调用缓冲区的值进行扫描
  96.    {
  97.       case 0: P2 = ((P2&0x1f)|0xc0); P0 = 0x01;
  98.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  99.       case 1: P2 = ((P2&0x1f)|0xc0); P0 = 0x02;
  100.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  101.       case 2: P2 = ((P2&0x1f)|0xc0); P0 = 0x04;
  102.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  103.       case 3: P2 = ((P2&0x1f)|0xc0); P0 = 0x08;
  104.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  105.       case 4: P2 = ((P2&0x1f)|0xc0); P0 = 0x10;
  106.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  107.       case 5: P2 = ((P2&0x1f)|0xc0); P0 = 0x20;
  108.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  109.       case 6: P2 = ((P2&0x1f)|0xc0); P0 = 0x40;
  110.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  111.       case 7: P2 = ((P2&0x1f)|0xc0); P0 = 0x80;
  112.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  113.    }
  114. }</font>
复制代码

本章资料下载若无黑币可前往原文
本章的内容到这里就结束了,蓝桥杯中我们需要用到的DS1302的介绍到这也就告一段落了,后面我们会逐步介绍iic、串口通信等内容。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak’s blog http://www.bkoak.com

联系博主
邮箱:wjf418224424@aliyun.com
更多资讯关注公众号:智术灵慧(zslh_dyh)
QQ交流群:693211073



DS1302例程.rar

32.58 KB, 下载次数: 8, 下载积分: 黑币 -5

回复

使用道具 举报

32#
ID:231500 发表于 2018-2-25 00:19 | 只看该作者
【蓝桥杯单片机设计与开发】(14)IIC总线概念简述IIC总线协议1)IIC总线的概念
IIC总线是一种串行总线,用于连接微控制器及其外围设备,具有以下特点:
①两条总线线路:一条串行数据线(SDA),一条串行时钟线(SCL)
②每个连接到总线的器件都可以使用软件更具它的唯一的地址来识别
③传输数据的设备间是简单的主从关系
④主机可以用作主机发送器或主机接收器
⑤它是一个多主机总线,两个或多个主机同时发起数据传输时,可以通过冲突检测和仲裁来方式数据被破坏
⑥串行的8位双向数据传输,位速率在标准模式下可达100kbit/s,在快速模式下可达400kbit/s,在高速模式下可达3.4Mbit/s(我们是模拟的一般标准速度就算不错)
⑦片上的滤波器可以增加干扰功能,保证数据的完整
⑧连接到同一总线上的IC数量受到总线最大电容的限制
发送器:发送数据到总线的器件
接收器:从总线接收数据的器件
主机:发起/停止数据传输、提供时钟信号的器件
从机:被主机寻址的器件
多主机:可以有多个主机试图去控制总线,但是不会破坏数据
仲裁:当多个主机试图去控制总线时,通过仲裁可以使得只有一个主机获得总线控制权,并且它传输的信息不会被破坏
同步:多个器件同步时钟信号的过程
I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关系。
每个接到I2C总线上的器件都有唯一的地址。主机与其它器件间的数据传送可以是由主机发送数据到其它器件,这时主机即为发送器。由总线上接收数据的器件则为接收器。
在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱, I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。
在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况。
数据位的有效性规定:
I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
2)IIC总线的信号类型
IIC总线在传送数据过程中共有3种类型信号:开始信号、结束信号和响应信号
①开始信号(S):SCL为高电平时,SDA由高电平向低电平跳变,表示起始信号,开始传送数据
②结束信号(P):SCL为高电平时,SDA由低电平向高电平跳变,表示结束信号,结束传送数据
③响应信号(ACK):接收器在接收到8位数据后,在第9个时钟周期,拉低SDA电平。即接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
开始信号S和结束信号P
起始和终止信号都是由主机发出的,在起始信号产生后,总线就处于被占用的状态;在终止信号产生后,总线就处于空闲状态。
连接到I2C总线上的器件,若具有I2C总线的硬件接口,则很容易检测到起始和终止信号。对于不具备I2C总线硬件接口的有些单片机来说,为了检测起始和终止信号,必须保证在每个时钟周期内对数据线SDA采样两次。  
接收器件收到一个完整的数据字节后,有可能需要完成一些其它工作,如处理内部中断服务等,可能无法立刻接收下一个字节,这时接收器件可以将SCL线拉成低电平,从而使主机处于等待状态。直到接收器件准备好接收下一个字节时,再释放SCL线使之为高电平,从而使数据传送可以继续进行。
应答信号ACK
3)IIC总线的数据传输格式
发送到SDA线上的每个字节必须是8位的,每次传输可以发送的字节数量不受限制。每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。
如果从机要完成一些其他功能后才能继续接收或发送,从机可以拉低SCL迫使主机进入等待状态。当从机准备好接收并释放SCL后,数据继续传输。如果主机在传输数据期间也需要完成一些其他功能叶可以拉低SCL以占住总线。
启动一个传输时,主机先发出S信号,然后发出8位数据。前7位为从机地址,第8位表示传输方向(0表示写操作,1表示读操作)。被选中的从机发出响应信号。跟着传输一系列字节及响应位。最后,主机发出P信号结束。
由于某种原因从机不对主机寻址信号应答时(如从机正在进行实时性的处理工作而无法接收总线上的数据),它必须将数据线置于高电平,而由主机产生一个终止信号以结束总线的数据传送。
如果从机对主机进行了应答,但在数据传送一段时间后无法继续接收更多的数据时,从机可以通过对无法接收的第一个数据字节的“非应答”通知主机,主机则应发出终止信号以结束数据的继续传送。
当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。
下列三种情况不会有ACK信号:
①当从机不能响应从机地址时(从机忙于其他事无法响应IIC总线操作或这个地址没有对应从机),在第9个SCL周期内SDA线没有被拉低,即没有ACK信号。这时,主机发送一个P信号终止传输或者重新发送一个S信号开始新的传输
②从机接收器在传输过程中不能接收更多的数据时,也不会发出ACK信号。主机意识到这点,从而发出一个P信号终止传输或者从新发送一个S信号开始新的传输
③主机接收器在接收到最后一个字节时,也不会发出ACK信号,于是,从机发送器释放SDA线,允许主机发送P信号结束传输
4)IIC总线的数据帧格式
I2C总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
在总线的一次数据传送过程中,可以有以下几种组合方式:
a、主机向从机发送数据,数据传送方向在整个传送过程中不变:
注:有阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送。
A表示应答, 表示非应答(高电平)。S表示起始信号,P表示终止信号。
b、主机在第一个字节后,立即由从机读数据
c、在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相。
5)IIC总线的寻址
I2C总线协议有明确的规定:采用7位的寻址字节(寻址字节是起始信号后的第一个字节)。
(1)寻址字节的位定义
D7~D1位组成从机的地址。D0位是数据传送方向位,为“0”时表示主机向从机写数据,为“1”时表示主机由从机读数据。
主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,如果相同,则认为自己正被主机寻址,根据R/位将自己确定为发送器或接收器。
从机的地址由固定部分和可编程部分组成。在一个系统中可能希望接入多个相同的从机,从机地址中可编程部分决定了可接入总线该类器件的最大数目。如一个从机的7位寻址位有4位是固定位,3位是可编程位,这时仅能寻址8个同样的器件,即可以有8个同样的器件接入到该I2C总线系统中。
(2)寻址字节中的特殊地址
其中高四位为器件类型识别符(即固定地址编号0000和1111,不同的芯片类型有不同的定义,EEPROM一般应为1010),接着三位为片选,最后一位为读写位,当为1时为读操作,为0时为写操作。
起始信号后的第一字节的8位为“0000 0000”时,称为通用呼叫地址。通用呼叫地址的用意在第二字节中加以说明。
格式为:
第二字节为 06H时,所有能响应通用呼叫地址的从机器件复位,并由硬件装入从机地址的可编程部分。能响应命令的从机器件复位时不拉低SDA和SCL线,以免堵塞总线。
第二字节为 04H时,所有能响应通用呼叫地址并通过硬件来定义其可编程地址的从机器件将锁定地址中的可编程位,但不进行复位。
如果第二字节的方向位B为“1”,则这两个字节命令称为硬件通用呼叫命令。
在这第二字节的高7位说明自己的地址。接在总线上的智能器件,如单片机或其他微处理器能识别这个地址,并与之传送数据。硬件主器件作为从机使用时,也用这个地址作为从机地址。
格式为:
在系统中另一种选择可能是系统复位时硬件主机器件工作在从机接收器方式,这时由系统中的主机先告诉硬件主机器件数据应送往的从机器件地址,当硬件主机器件要发送数据时就可以直接向指定从机器件发送数据了。
(3)起始字节
起始字节是提供给没有I2C总线接口的单片机查询I2C总线时使用的特殊字节。
不具备I2C总线接口的单片机,则必须通过软件不断地检测总线,以便及时地响应总线的请求。单片机的速度与硬件接口器件的速度就出现了较大的差别,为此,I2C总线上的数据传送要由一个较长的起始过程加以引导。
引导过程由起始信号、起始字节、应答位、重复起始信号(Sr)组成。
请求访问总线的主机发出起始信号后,发送起始字节(0000 0001),另一个单片机可以用一个比较低的速率采样SDA线,直到检测到起始字节中的7个“0”中的一个为止。在检测到SDA线上的高电平后,单片机就可以用较高的采样速率,以便寻找作为同步信号使用的第二个起始信号Sr。
80C51单片机I2C串行总线器件的接口1)总线数据传送的模拟
主机可以采用不带I2C总线接口的单片机,如80C51、AT89C2051等单片机,利用软件实现I2C总线的数据传送,即软件与硬件结合的信号模拟。
(1)典型信号模拟
(2)典型信号模拟子程序
①起始信号
[backcolor=rgb(15, 25, 42) !important][size=1em]
[size=1em]01

[size=1em]02

[size=1em]03

[size=1em]04

[size=1em]05

[size=1em]06

[size=1em]07

[size=1em]08

[size=1em]09

[size=1em]10

[size=1em][size=1em]void T2CStart(void)
[size=1em]{
[size=1em]   SomeNop();
[size=1em]   SCL = 1;
[size=1em]   SomeNop();
[size=1em]   SDA = 0;
[size=1em]   SomeNop();
[size=1em]   SCL = 0;
[size=1em]   SomeNop();
[size=1em]}



②终止信号
[backcolor=rgb(15, 25, 42) !important][size=1em]
[size=1em]01

[size=1em]02

[size=1em]03

[size=1em]04

[size=1em]05

[size=1em]06

[size=1em]07

[size=1em]08

[size=1em]09

[size=1em]10

[size=1em][size=1em]void I2cStop(void)
[size=1em]{
[size=1em]   SDA = 0;
[size=1em]   SomeNop();
[size=1em]   SCL = 1;
[size=1em]   SomeNop();
[size=1em]   SDA = 1;
[size=1em]   SomeNop();
[size=1em]   SCL = 1;
[size=1em]}



本章没有需要下载的资料
本章的内容到这里就结束了,蓝桥杯中我们需要用到的IIC总线的器件有两个一个是AT24C02另一个是PCF8591,一个是储存芯片一个是AD转换芯片,这两个芯片的应用我们在后续的教程中也会进一步讲解。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak’s blog http://www.bkoak.com

联系博主
邮箱:wjf418224424@aliyun.com
更多资讯关注公众号:智术灵慧(zslh_dyh)
QQ交流群:693211073


回复

使用道具 举报

33#
ID:231500 发表于 2018-2-26 00:07 | 只看该作者
【蓝桥杯单片机设计与开发】(15)IIC总线驱动的编写及注释
后学我们将讲到的AT24C02和PCF8591芯片都是使用的IIC总线协议,故本章末尾会将这两个芯片需要使用到的一些函数一并引出,在后续章节进行详细讲解。
本章程序的注释不多,希望大家多对照上一章的内容进行理解。
  1. #ifndef _IIC_H
  2. #define _IIC_H

  3. #include "stc15f2k60s2.h"
  4. #include "intrins.h"

  5. #define uchar unsigned char
  6. #define uint unsigned int

  7. // 总线引脚定义
  8. sbit SDA = P2^1; /* 数据线 */
  9. sbit SCL = P2^0; /* 时钟线 */

  10. void Delay5us() //@11.0592MHz
  11. {
  12.    uchar i;
  13.    
  14.    _nop_();
  15.    i = 11;
  16.    while (--i);
  17. }

  18. void Delay10ms() //@11.0592MHz
  19. {
  20.    uchar i, j;

  21.    i = 108;
  22.    j = 145;
  23.    do
  24.    {
  25.       while (--j);
  26.    } while (--i);
  27. }

  28. // 总线启动条件
  29. void IIC_Start(void)
  30. {
  31.    SDA = 1;
  32.    SCL = 1;
  33.    Delay5us();
  34.    SDA = 0;
  35.    Delay5us();
  36.    SCL = 0;
  37. }

  38. // 总线停止条件
  39. void IIC_Stop(void)
  40. {
  41.    SDA = 0;
  42.    SCL = 1;
  43.    Delay5us();
  44.    SDA = 1;
  45. }

  46. // 应答位控制
  47. void IIC_Ack(bit ackbit)
  48. {
  49.    if (ackbit)
  50.    {
  51.       SDA = 0;
  52.    }
  53.    else
  54.    {
  55.       SDA = 1;
  56.    }
  57.    Delay5us();
  58.    SCL = 1;
  59.    Delay5us();
  60.    SCL = 0;
  61.    SDA = 1;
  62.    Delay5us();
  63. }

  64. // 等待应答
  65. bit IIC_WaitAck(void)
  66. {
  67.    SDA = 1;
  68.    Delay5us();
  69.    SCL = 1;
  70.    Delay5us();
  71.    if (SDA)
  72.    {
  73.       SCL = 0;
  74.       IIC_Stop();
  75.       return 0;
  76.    }
  77.    else
  78.    {
  79.       SCL = 0;
  80.       return 1;
  81.    }
  82. }

  83. // 通过 I2C 总线发送数据
  84. void IIC_SendByte(uchar byt)
  85. {
  86.    uchar i;
  87.    for (i = 0;i < 8;i++)
  88.    {
  89.       if (byt & 0x80)
  90.       {
  91.          SDA = 1;
  92.       }
  93.       else
  94.       {
  95.          SDA = 0;
  96.       }
  97.       Delay5us();
  98.       SCL = 1;
  99.       byt <<= 1;
  100.       Delay5us();
  101.       SCL = 0;
  102.    }
  103. }

  104. // 从 I2C 总线上接收数据
  105. uchar IIC_RecByte(void)
  106. {
  107.    uchar da;
  108.    uchar i;
  109.    
  110.    for (i = 0;i < 8;i++)
  111.    {
  112.       SCL = 1;
  113.       Delay5us();
  114.       da <<= 1;
  115.       if (SDA)
  116.          da |= 0x01;
  117.       SCL = 0;
  118.       Delay5us();
  119.    }
  120.    return da;
  121. }

  122. //PCF8591初始化
  123. void ADC_Init(uchar chanel)
  124. {
  125.    IIC_Start();      //IIC启动信号
  126.    IIC_SendByte(0x90);  //1001 000 0;选中该器件(前7位是地址,最后一位是R/~W,表示写)
  127.    IIC_WaitAck();  //等待应答
  128.    IIC_SendByte(chanel);//通过IIC发送通道
  129.    IIC_WaitAck();  //等待应答
  130.    IIC_Stop();  //IIC停止信号
  131.    Delay10ms();      //等待10ms
  132. }

  133. //读取PCF8591
  134. uchar ADC_Read()
  135. {
  136.    uchar temp;
  137.    IIC_Start();     //IIC启动信号
  138.    IIC_SendByte(0x91); //1001 000 1;选中该器件(前7位是地址,最后一位是R/~W,表示读)
  139.    IIC_WaitAck(); //等待应答
  140.    temp=IIC_RecByte(); //通过IIC读取采集的值
  141.    IIC_Ack(0); //通过IIC发送应答
  142.    IIC_Stop(); //IIC停止信号
  143.    return temp;
  144. }

  145. void WriteByte_AT24C02(uchar add,uchar date)
  146. {
  147.    IIC_Start();
  148.    IIC_SendByte(0xa0);//发方式字1010 0000
  149.    IIC_WaitAck();
  150.    IIC_SendByte(add);
  151.    IIC_WaitAck();
  152.    IIC_SendByte(date);
  153.    IIC_WaitAck();
  154.    IIC_Stop();
  155.    Delay10ms();
  156. }

  157. uchar ReadByte_AT24C02(uchar add)
  158. {
  159.    uchar date;

  160.    IIC_Start();
  161.    IIC_SendByte(0xa0);//发方式字1010 0000
  162.    IIC_WaitAck();
  163.    IIC_SendByte(add);
  164.    IIC_WaitAck();

  165.    IIC_Start();
  166.    IIC_SendByte(0xa1);//发方式字1010 0001
  167.    IIC_WaitAck();
  168.    date = IIC_RecByte();
  169.    IIC_Ack(0);
  170.    IIC_Stop();
  171.    return date;
  172. }
复制代码
本章资料下载见原文。

本章关于IIC总线的驱动程序就如上文所示,再有四章内容我们将AT24C02和PCF8591芯片的功能原理以及简单的程序运用讲完,该教材将暂停5天左右,最晚于3月五号继续更新,讲解历年省赛真题内容。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak’s blog  http://www.bkoak.com


联系博主
邮箱:wjf418224424@aliyun.com
更多资讯关注公众号:智术灵慧(zslh_dyh)
QQ交流群:693211073


回复

使用道具 举报

34#
ID:231500 发表于 2018-3-1 20:52 | 只看该作者
【蓝桥杯单片机设计与开发】(16)AT24C02串行E2PROM原理讲解及程序编写
EEPROM的使用,AT24C02,可以掉电依旧保存上一次操作的数据,下次上电后接着运行。
需要注意两个函数,一个是写进EEPROM里面保存,再次上电再从里面读回来,其中写函数需要指定AT24C02的地址以及需要写入的数据,读函数要想取回写进的数据,需要从相同的地址里面读,其中AT24C02的存储地址是0x00,可以是其他地址,如0x02,但是读和写的地址必须一致由芯片资料及原理图知EEPROM(AT24C02)的写地址为0xa0;读地址为0xa1。(读写函数见上章)
注意:读数的时候读出一个数之后发送一个应答信号,若ACK(0)表示还想继续读下一个字节,若ACK(1);则不想再读数,让EEPROM停止发送。
示例程序
  1. /* 2017.2.25 */
  2.    /* www.bkoak.com */
  3.    /* AT24C02读写例程 */

  4. #include "stc15f2k60s2.h"
  5. #include "IIC.h"

  6. uchar code tab[] =
  7. {0XC0,0XF9,0XA4,0XB0,0X99,0X92,
  8. 0X82,0XF8,0X80,0X90,0XBF,0XFF};
  9. uchar strtab[2];
  10. uchar buffer;

  11. void temp_to_display(uchar temp);
  12. void Timer1Init(void);

  13. void main()
  14. {
  15.    P2 = ((P2&0x1f)|0xa0);
  16.    P0 = 0x00;
  17.    P2 = ((P2&0x1f)|0xc0);
  18.    P0 = 0x00;

  19.    Timer1Init();
  20.    ET1 = 1;
  21.    EA = 1;
  22.    
  23.    WriteByte_AT24C02(0x00,15);

  24.    while(1)
  25.    {
  26.       buffer = ReadByte_AT24C02(0x00);
  27.       temp_to_display(buffer);
  28.    }
  29. }

  30. void temp_to_display(uchar temp)
  31. {
  32.    strtab[0] = tab[(temp / 100) % 10];
  33.    strtab[1] = tab[(temp / 10) % 10];
  34.    strtab[2] = tab[temp % 10];
  35. }

  36. void Timer1Init(void) //1毫秒@11.0592MHz
  37. {
  38.    AUXR |= 0x40; //定时器时钟1T模式
  39.    TMOD &= 0x0F; //设置定时器模式
  40.    TL1 = 0x66; //设置定时初值
  41.    TH1 = 0xEA; //设置定时初值
  42.    TF1 = 0; //清除TF1标志
  43.    TR1 = 1; //定时器1开始计时
  44. }

  45. void Timer1_int() interrupt 3 using 0
  46. {
  47.    static uchar num;
  48.    TH1 = 0x66;
  49.    TL1 = 0xea;

  50.    switch(num)//分别调用缓冲区的值进行扫描
  51.    {
  52.       case 0: P2 = ((P2&0x1f)|0xc0); P0 = 0x20;
  53.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  54.       case 1: P2 = ((P2&0x1f)|0xc0); P0 = 0x40;
  55.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  56.       case 2: P2 = ((P2&0x1f)|0xc0); P0 = 0x80;
  57.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  58.       default:break;
  59.    }
  60.    num++;
  61.    if (num == 3)
  62.       num = 0;
  63. }
复制代码

【蓝桥杯单片机设计与开发】(17)PCF8591 8位A/D和D/A转换器转换原理讲解及程序编写
这里我们只需要了解一下PCF8591的时序格式,只需根据时序格式发送地址字节和控制字节。
当使用AD转换的时候需要在main函数开始时调用,该函数内部只需看这句代码即可:i2c_sendbyte(0x03);ADC通道3,板上有4个模拟输入口,分别为0,1,2,3;设置哪一个模拟输入口就是根据这句代码,0x03表示通道3,8位前6位不用管,都为0,最后两位就是配置选择哪一个通道的。
地址字节,其中A2,A1,A0硬件已经接地,故都为0,最低位表示的是你要从IIC总线上读数还是写数据,1表示读,0表示写,即读数据发的地址是:0x91;写数据发的地址是0x90。
控制字节,由芯片资料知,控制字节有8位,有两位固定是0,除了第0、1位需要自己设置,其他的我们都设为0,那些位都是一些具体的功能,我们暂时用不着,不用管先,第0、1位是模拟通道选择,PCF8591上提供了4路模拟通道,根据需求进行选择,如选择通道3即发送控制字节:0x03。
示例程序
  1. /* 2017.2.25 */
  2.    /* www.bkoak.com */
  3.    /* PCF8591AD例程 */

  4. #include "stc15f2k60s2.h"
  5. #include "IIC.h"

  6. uchar code tab[] =
  7. {0XC0,0XF9,0XA4,0XB0,0X99,0X92,
  8. 0X82,0XF8,0X80,0X90,0XBF,0XFF};
  9. uchar strtab[2];

  10. void temp_to_display(uchar temp);
  11. void Timer1Init(void);

  12. void main()
  13. {
  14.    P2 = ((P2&amp;0x1f)|0xa0);
  15.    P0 = 0x00;
  16.    P2 = ((P2&amp;0x1f)|0xc0);
  17.    P0 = 0x00;

  18.    Timer1Init();
  19.    ET1 = 1;
  20.    EA = 1;

  21.    ADC_Init(0x03);
  22.    while(1)
  23.    {
  24.       temp_to_display(ADC_Read());
  25.    }
  26. }

  27. void temp_to_display(uchar temp)
  28. {
  29.    strtab[0] = tab[(temp / 100) % 10];
  30.    strtab[1] = tab[(temp / 10) % 10];
  31.    strtab[2] = tab[temp % 10];
  32. }

  33. void Timer1Init(void) //1毫秒@11.0592MHz
  34. {
  35.    AUXR |= 0x40; //定时器时钟1T模式
  36.    TMOD &= 0x0F; //设置定时器模式
  37.    TL1 = 0x66; //设置定时初值
  38.    TH1 = 0xEA; //设置定时初值
  39.    TF1 = 0; //清除TF1标志
  40.    TR1 = 1; //定时器1开始计时
  41. }

  42. void Timer1_int() interrupt 3 using 0
  43. {
  44.    static uchar num;
  45.    TH1 = 0x66;
  46.    TL1 = 0xea;

  47.    switch(num)//分别调用缓冲区的值进行扫描
  48.    {
  49.       case 0: P2 = ((P2&0x1f)|0xc0); P0 = 0x20;
  50.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  51.       case 1: P2 = ((P2&0x1f)|0xc0); P0 = 0x40;
  52.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  53.       case 2: P2 = ((P2&0x1f)|0xc0); P0 = 0x80;
  54.               P2 = ((P2&0x1f)|0xff); P0 = strtab[num];break;
  55.       default:break;
  56.    }
  57.    num++;
  58.    if (num == 3)
  59.       num = 0;
  60. }
复制代码

本章资料下载地址见原文。
           希望接下来的几天大家好好复习一下前面章节的内容,对比赛做好准备,3.5-3.15号之间为大家更新客观题和省赛综合题的内容。

本文采用 「CC BY-NC-SA 4.0」创作共享协议,转载请标注以下信息:
原文出处:Bkoak’s blog  http://www.bkoak.com

联系博主
邮箱:wjf418224424@aliyun.com
更多资讯关注公众号:智术灵慧(zslh_dyh)
QQ交流群:693211073


回复

使用道具 举报

35#
ID:231500 发表于 2018-3-1 20:53 | 只看该作者
蓝桥杯单片机开发省赛基础部分就到此结束了,后面会开新帖进行客观题和历年省赛的真题及模拟题的讲解,有问题可以进群或邮箱留言。
回复

使用道具 举报

36#
ID:220062 发表于 2018-3-2 10:49 | 只看该作者
冒泡看一下
回复

使用道具 举报

37#
ID:286874 发表于 2018-3-2 11:09 | 只看该作者
谢谢楼主
回复

使用道具 举报

38#
ID:231500 发表于 2018-3-6 18:42 | 只看该作者
回复

使用道具 举报

39#
ID:231500 发表于 2018-10-11 22:43 | 只看该作者
新一年的比赛报名已经开始,我会继续更新一些教程,把往年的省赛国赛题目都作出历程给大家,,同时后面还有一个AD18的教学贴和开源POV玩具的制作贴会慢慢更新,有兴趣的朋友可以关注公众号或者加入交流群,大家一起交流学习。
回复

使用道具 举报

40#
ID:294964 发表于 2018-12-4 17:12 | 只看该作者
干货,支持!!!!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表