极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 125921|回复: 31

Arduino教程 - I2C模块的使用 - 集成DS1307时钟芯片、AT24C32存储芯片、LM75温度侦测

[复制链接]
发表于 2013-9-4 21:34:41 | 显示全部楼层 |阅读模式
本帖最后由 sbsdys 于 2013-9-4 22:28 编辑

本站内有一篇类似的文章,用的是淘宝上很常见的一个I2C模块,帖子地址:
http://www.geek-workshop.com/thread-207-1-1.html

下面再介绍一个新的I2C模块,区别于刚提到的那个帖子,这个模块使用了新的库,令开发更加简单,并且最新集成了使用I2C总线的LM75温度传感器芯片,而不是使用大家常见的DS18B20。

本帖中所要用到的所有资料(包括库文件、源代码、芯片数据手册等):



记得在开始使用代码文件时,先把库文件放到Arduino IDE的“libraries”目录下,所有例程在Arduino 1.0.3下编译通过

先上图,新入手的I2C模块:




一、先来说说实时时钟DS1307的使用!

DS1307 是一款十分常用的实时时钟芯片,它可以记录年、月、日、时、分、秒等信息,提供至2100年的记录。可使用电池供电,也就是说,即使Arduino 在断电状态下,时钟芯片仍然是在运行的。它使用十分常用的两线式串行总线(I2C),只要两根线即可和Arduino 通信。

接线图:


运行Arduino IDE,打开以下文件:


可以看到代码:
  1. #include <Wire.h>
  2. #include <RTClib.h>

  3. void printDateTime(DateTime dateTime);

  4. //创建实例
  5. RTC_DS1307 RTC;

  6. void setup (void){
  7.   Serial.begin(9600);
  8.   //初始化总线
  9.   Wire.begin();
  10.   //初始化实时时钟
  11.   RTC.begin();   
  12.    
  13. }

  14. void loop() {
  15.   if (Serial.available() > 0) {

  16.     int instruct = Serial.read();

  17.     switch (instruct) {
  18.     case 'D': {
  19.       //获取当前日期和时间
  20.       DateTime now = RTC.now();
  21.       //通过串口传送当前的日期和时间      
  22.       printDateTime(now);
  23.       break;
  24.     } case 'S':
  25.       //设置成6月
  26.       RTC.set(RTC_MONTH, 6);
  27.       //设置成16点
  28.       RTC.set(RTC_HOUR, 16);
  29.       break;
  30.     }
  31.   }
  32. }

  33. void printDateTime(DateTime dateTime) {
  34.     //传送年份
  35.     Serial.print(dateTime.year(), DEC);
  36.     Serial.print('/');
  37.     //传送月份
  38.     Serial.print(dateTime.month(), DEC);
  39.     Serial.print('/');
  40.     //传送月份中的第几天
  41.     Serial.print(dateTime.day(), DEC);
  42.     Serial.print(' ');
  43.     //传送小时
  44.     Serial.print(dateTime.hour(), DEC);
  45.     Serial.print(':');
  46.     //传送分钟
  47.     Serial.print(dateTime.minute(), DEC);
  48.     Serial.print(':');
  49.     //传送秒
  50.     Serial.print(dateTime.second(), DEC);
  51.     Serial.println();
  52. }
复制代码


编译并上传到Arduino,打开串口监控器,当输入字符“D”时,会返回当前的时间:


而当输入字符“S”时,则会设置RTC为代码中指定的时间。输入“S”后再输入“D”,显示为代码中指定的时间了:



下面我们来看一下代码:

#include <Wire.h>
   

Arduino 与实时时钟通讯是通过I2C总线,为了使用这个总线,必须告知编译器加入这个库。 #include 是一种预编译指令,它告知编译器,在编译代码时加入Wire.h 这个文件,而这个文件则包含了我们使用I2C总线的所有函数,就是所谓的库。而紧接着的 #include <RTClib.h> 也是一样的意思。


Wire.begin();

begin() 则是Wire实例的一个方法,作用是初始化芯片的I2C总线,这个初始化只要一次就行,即使你有多个I2C设备也是如此,所以我们把它放在setup()里面。紧接着的RTC.begin() 也是一样的意思。


RTC_DS1307 RTC;

这是本章比较难理解的地方,RTC_DS1307 是实时时钟库的一个类,而后面的RTC,则是创建一个RTC_DS1307类的实例,这个实例包括一些相关的函数和变量。例如adjust() 是RTC_DS1307的一个方法,你可以通过实例来调用它,即RTC.adjust()

DateTime now =RTC.now();

now() 是RTC_DS1307 的另一个方法,它返回一个DateTime实例,保存着当前的日期时间信息,运行这条语句之后我们可以通过now.month() 获知当前的月份, 通过now.minute() 获取当前的分钟数,如此类推year、day、hour、second。

printDateTime(now);

printDateTime() 是一个把当前时间通过串口传送回电脑的函数,它接收一个Datatime 类型的参数。

RTC.set(RTC_MONTH, 6);

这条语句的作用是设置实时时钟的月份为6月,set()是一个专门设置实时时钟的函数,它接收两个参数,一个是设置的类别,这里的RTC_MONTH 表示月份,相应的还有 RTC_YEAR、RTC_DAY、RTC_MINUTE、RTC_SECOND。第二个参数这是设置的值、这里的6表示6月。

Serial.print(dateTime.year(), DEC);

这条语句的意思是把月份的数值通过串口传送回电脑。 print 是Serial(串口)实例的一个方法,它接收两个参数,第一个是传送的值,第二个是传送的格式,DEC表示10进制,还有OCT 表示8进制,HEX 被表示16进制。

第一部分就到这里,谢谢:-)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2013-9-4 21:50:38 | 显示全部楼层
谢谢 sbsdys。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-9-4 21:59:32 | 显示全部楼层
二、外部存储:AT24C32存储芯片的使用

AT24C32提供32768位的串行电可擦写可编程只读存储器,组织形式为4096字×8位字长。AT24C32的级联特性允许在同一I2C总线上挂接8个器件,百万次的复写周期和100年的保存年限,并带有写保护功能。

接线图(和RTC DS1307的接法一样):


运行Arduino IDE,打开以下文件:


可以在Arduino IDE中看到以下代码:

  1. #include <Wire.h>
  2. #include <AT24Cxx.h>
  3. #include <RTClib.h>

  4. AT24Cxx AT24C32(0x50);
  5. RTC_DS1307 RTC;

  6. void setup (void){
  7.   //初始化串口
  8.   Serial.begin(9600);
  9.   //初始化I2C
  10.   Wire.begin();
  11.   //初始化RTC
  12.   RTC.begin();

  13. }

  14. void loop() {
  15.   //判断是否有新串口数据
  16.   if (Serial.available() > 0) {
  17.     //读取数据
  18.     int instruct = Serial.read();

  19.     switch (instruct) {
  20.     case 'P':
  21.       {
  22.         //先eeprom 指定地址写入数据
  23.         AT24C32.WriteMem(0, 0x04);
  24.         break;
  25.       }
  26.     case 'G':
  27.       {
  28.         char buffer[3];
  29.         //读取三个字节数据
  30.         AT24C32.ReadMem(0, buffer, 3);
  31.         Serial.print(2000 + buffer[2], DEC);
  32.         Serial.print('/');
  33.         Serial.print(buffer[1], DEC);
  34.         Serial.print('/');
  35.         Serial.print(buffer[0], DEC);
  36.         Serial.println();
  37.         break;
  38.       }
  39.     case 'F':
  40.       {
  41.         //获取当前时间信息
  42.         DateTime now = RTC.now();
  43.         char buffer[3];
  44.         buffer[0] = now.day();
  45.         buffer[1] = now.month();
  46.         buffer[2] = now.year() - 2000;
  47.         //写入时间信息到eeprom
  48.         AT24C32.WriteMem(0, buffer, 3);
  49.         break;
  50.       }
  51.     }
  52.   }
  53. }
复制代码


打开串口监控器,当输入字符“F”时,串口窗口不会有任何反应,但Arduino会把年、月、日信息储存到EEPROM;输入字符“G”,则会把存储的年、月、日信息读取出来。复位Arduino后再次输入字符“G”,仍能把一样的信息读取出来,说明数据没有丢失。
输入字符“P”,再输入字符“G”,可以看到日期被强制设定为4号


下面我们来看一下代码:

#include <Wire.h>#include <AT24Cxx.h>

当设备要使用I2C总线时,必须包含Wire.h 这个文件。而AT24Cxx.h 则是AT24Cxx 系列通用的头文件。

AT24Cxx AT24C32(0x50);

创建一个AT24Cxx 实例,而0x50则是AT24C32的地址,上面介绍AT24C32 时提到这款芯片可以在I2C总线上挂接8个器件,那么我们如何区分开来这八个器件呢? AT24C32又是如何知道哪些数据是发给自己而不是别的总线上的器件呢?
                              
原来,AT24C32上还配备了三个地址选定管脚,A2、A1、A0。而高四位的地址已经固定为1010,A2、A1、A0三个地址脚的连接方式分别决定了后3位的数值。I2C模块上的AT24C32的A2、A1、A0 都接到了地,所以得出的7位地址为101 0000,写成16进制的话为0x50。

char buffer[3];
buffer[0] = now.day();
buffer[1] = now.month();
buffer[2] = now.year() - 2000;
AT24C32.WriteMem(0, buffer, 3);

这几行的代码的意思是把年、月、日信息写到EEPROM上,我们首先看WriteMem 这个方法,它是专门用来写(保存)数据到EEPROM的,它接收三个参数,第一个是地址,EEPROM内部是通过地址来区分数据存储位置的,例如AT24C32,它具有4096字×8位字长的存储能力,则地址范围则是0 - 4096。这里第一个参数设的是“0”,他可以更改为0 - 4096中的任意一个数。第二个参数为需要写到存储器的数据块的开头地址,注意不是数据本身,而是数据的地址,比较容易理解的实现方式是声明一个数组,然后把数组名作为这个参数(数组名则为该数据块的起始地址)。最后一个参数是要写入的字节数,它不能大于数据块的大小(即数组的大小)。

AT24C32.WriteMem(0, 0x04);

这行代码的意思是把一字节的数据(0x04)写到EEPROM的地址为0的空间上,这里大家会发现这个方法的名字跟前面的一模一样,这是面向对象编程中一个叫重载的特性,这项特性允许创建函数名称相同但功能的输入输出类型不同的子程序,当你只要存储一个字节的数据时比较方便。

char buffer[3];AT24C32.ReadMem(0, buffer, 3);

这两行代码的意思是从EEPROM上读取三个字节的数据,并保存到缓冲数组上。ReadMem这个方法是专门把数据从EEPROM上读取出来。它接收三个参数,第一个是地址,第二个是缓冲块的开头地址(数组名),最后一个参数是要读取的字节数,它不能大于缓冲数据块的大小(即数组的大小)。

第二部分到此结束:-)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-9-4 22:11:45 | 显示全部楼层
三、温度侦测芯片LM75的使用

LM75是一款十分常用的温度侦测器件。它能够提供0.125℃的精度,多种温度范围选择,并提供过温输出接口。它使用I2C总线进行通讯,配合自身的地址选择,可以在同一总线上挂接8个设备。

接线图:

这里比之前要多接一根线,用来侦测LM75的温度报警输出。

运行Arduino IDE,打开以下文件:


可以在Arduino IDE中看到以下代码:

  1. #include <Wire.h>
  2. #include <LM75.h>

  3. LM75 sensor(LM75_ADDRESS | 0b000);
  4. const int OSPIN = 6;

  5. void setup (void){
  6.   //设置OS管脚为输入
  7.   pinMode(OSPIN, INPUT);
  8.   //初始化串口
  9.   Serial.begin(9600);
  10.   //初始化I2C
  11.   Wire.begin();
  12.   //设置报警温度
  13.   sensor.tos(47.5);
  14.   //设置迟滞温度
  15.   sensor.thyst(42);

  16. }

  17. void loop() {
  18.   //判断是否有新串口数据
  19.   if (Serial.available() > 0) {
  20.     //读取数据
  21.     int instruct = Serial.read();
  22.     int OSValue = HIGH;
  23.     //读取OS管脚电平
  24.     OSValue = digitalRead(OSPIN);
  25.     //如果OS管脚为低电平
  26.     if (OSValue == LOW)
  27.         //返回报警
  28.         Serial.println("Over heating!");

  29.     switch (instruct) {
  30.     case 'T':
  31.       Serial.print("Current temp: ");
  32.       //返回当前温度
  33.       Serial.print(sensor.temp());
  34.       Serial.println(" C");
  35.       break;
  36.     }
  37.   }
  38. }
复制代码


打开串口监控器,当输入字符“T”时,则会返回当前的环境温度值,当温度超过47.5°时,串口将会返回 "Over heating!" 的警告。


下面我们来看一下代码:

#include <LM75.h>

此为LM75系类通用的库文件。

LM75 sensor(LM75_ADDRESS | 0b000);

与AT24C32 类似,LM75 可以通过设置自身的的A2、A1、A0三个地址管脚来区分总线是否在与自己通讯,而高4 位的地址已经固定,我们的芯片上三个地址脚都接到了GND,所以为0b000。

pinMode(OSPIN,INPUT);

这行代码的意思是把OSPIN管脚(Pin 6),设置成输入,我们在第2章时,把LED管脚设成输出,用的是同样的函数,只是参数不一样,第2章用的是输出,这一章用的是输入,即INPUT。

sensor.tos(47.5);
sensor.thyst(42);

这两行代码的意思是设置报警温度(TOS)和滞回温度(THYST). 通过设置这两个温度,可以设定一个区间,使得当环境温度超过47.5℃时,LM75 的OS管脚将会输出一个低电平,当环境温度降至42℃以下时,这个管脚则会恢复高电平。注意,当温度一旦超过47.5°,则在它降至42°前,管脚是一直维持低电平的。

OSValue = digitalRead(OSPIN);

这行代码的意思是读取OSPIN管脚的电平值。digitalRead 使用来读取管脚电平的函数,它的作用刚好与digitalWrite 相反,后者用来输出电平。digitalRead函数只会返回两种类型的值: HIGH,表示高电平;LOW,表示低电平。


sensor.temp()

这行代码的意思是获取当前的温度。temp() 是sensor实例的一个方法,调用它即可返回当前的摄氏温度值。


到这里,模块上集成的三个功能的使用方法分享完毕,谢谢大家的围观{:soso_e113:}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-9-4 22:30:17 | 显示全部楼层
szpapas 发表于 2013-9-4 21:50
谢谢 sbsdys。

谢谢顶贴
回复 支持 反对

使用道具 举报

发表于 2013-9-5 08:16:26 | 显示全部楼层
怎么感觉 又像广告呢?
回复 支持 反对

使用道具 举报

发表于 2013-9-5 08:35:45 | 显示全部楼层
Cupid 发表于 2013-9-5 08:16
怎么感觉 又像广告呢?

恩,是有点像
但是,可以学到点东西
回复 支持 反对

使用道具 举报

发表于 2013-9-5 08:58:41 | 显示全部楼层
有图有代码有接线,广告也无妨啦
回复 支持 反对

使用道具 举报

发表于 2013-9-5 11:00:44 | 显示全部楼层
{:soso_e113:}技术贴广告还是很欢迎的。。。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-9-5 11:05:37 | 显示全部楼层
弘毅 发表于 2013-9-5 11:00
技术贴广告还是很欢迎的。。。

谢谢啦,欢迎大家一起交流
回复 支持 反对

使用道具 举报

发表于 2013-9-5 15:56:37 | 显示全部楼层
不错,介绍得很详细,初学者也可以看明白。支持。这个模块有没有购买链接啊?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-9-5 22:07:00 | 显示全部楼层
philhoo 发表于 2013-9-5 15:56
不错,介绍得很详细,初学者也可以看明白。支持。这个模块有没有购买链接啊?

在淘宝搜索“I2C模块”就可以找到啦,目前只有这个模块集成了使用I2C总线的温度侦测芯片哦。
回复 支持 反对

使用道具 举报

发表于 2013-9-5 23:39:15 | 显示全部楼层
个人赞成这种技术广告贴,起码能学习到一些东西
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-9-6 10:26:23 | 显示全部楼层
chqiyi 发表于 2013-9-5 23:39
个人赞成这种技术广告贴,起码能学习到一些东西

谢谢顶贴,本帖无意加入广告,欢迎交流:-)
回复 支持 反对

使用道具 举报

发表于 2013-11-11 09:43:00 | 显示全部楼层
请问那个ds1307的库可以提供一下么?
回复 支持 反对

使用道具 举报

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

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-24 18:14 , Processed in 0.047292 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

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