[an error occurred while processing this directive]
by Guido Socher (homepage)
关于作者:
Guido喜欢Linux,因为对于开发自己的硬件来说Linux确实是一个非常好的系统。
目录:
|
一个数字温度计或跟你的atmel微控制器进行I2C通信
摘要:
Atmel的Atmega8微控制器有充足的数字和模拟输入输出线。
对于开发任何种类的测量设备来说它确实是一个理想的器件。
在这篇文章中让我们来看一下如何不使用额外的MAX232芯片而只通过物理的RS232接口连接微控制器和Linux
PC.
_________________ _________________ _________________
|
概述
看这篇文章你必须有像在my "Programming the AVR
microcontroller with GCC, libc 1.0.4" article中描述的那样的GCC
AVR编程环境。如果想避免安装的麻烦你当然可以使用the AVR programming CD
(from http://shop.tuxgraphics.org/,
)
当你使用这么先进的器件作为一个微控制器来测量模拟或数字信号时,
你肯定需要一些接口来计算它产生的数据或者对它发送命令。
以前我们在此发布的所有文章中我们总是通过微控制器中的UART来使用RS232进行通信。
问题是那种方式需要一个额外的MAX232芯片和4个额外的电容器。
另外,Atmel还建议:UART通信的可靠工作还需要一个外部晶振。
不管怎样那需要好多的额外器件,我们可以想办法避免使用它们。
PC和微控制器之间所传送的数据量通常很少(仅仅几个字节),
因此速度一点都不重要。这就使得I2C总线(协议)非常适合这项工作。
I2C(I2C,读作I方C,英语读作eye-square-see)是一个双线双向的通信接口。
它是由菲利浦公司创造的,并且其名称也受到保护。
这也是为什么其他的厂商对同一个协议使用不同的名字。
Ateml管I2C叫TWI(Two Wire Interface,双线接口)。
你们好多人可能不知道在你们的电脑中已经使用了I2C总线。
所有流行的主板上都有一个I2C总线来读取温度、风扇转速以及可用的内存容量等等所有的硬件信息。
不幸的是这一I2C总线不能用于PC外部(没有物理的接插件)。
所以我们得发明一些新的方法。
但首先让我们来看一下TWI(另一个名字叫I2C)是怎样工作的。
I2C/TWI如何工作?
实际上Atmega8数据表(参见参考资料)从第160页起就有了一个非常详细的描述。
所以在这里我只提供一个总览。看过后你就应该明白数据表中描述的项目了。
在I2C总线上总会有一个主设备和一个或几个从设备。主设备初始化通信并控制时钟。
总线的两根线分别叫SDA(数据线)和SCL(时钟线)。
总线上的每一个设备必须有独立的电源(跟传统的RS232通信相同)。
通常两条线都通过4.7K的上拉电阻连接到逻辑高电平上(对于5V的IC是 +5V)。
这就给出了所有设备间的“或”连接。
当一个设备想传送“0”时只需要将一条线接地即可,或者传送“1”时保持高电平。
总线上每一个设备都有一个7位的唯一地址。
主设备通过送出一个称做“起始状态”的模式串开始一次通信,
然后再对其想访问的设备进行寻址。
接着主设备送出一个比特来说明它是想读还是写数据。
从设备回送一个应答位(ack-bit)表示它能明白主设备想干什么。
换言之我们在总线上看到了9个比特的数据(7位地址码+一个读写位+一个应答位)。
| start | 7-bit slave adr | read_data bit | wait for ack | ... data comes here
然后是什么?
下一步我们就可以收发数据了。传送的数据总是8比特(一个字节)的倍数并且必须收到应答位。
换句话说总线上的数据总是按9比特的包传送的。通信结束后主设备必须送出一个“停止状态”。
也就是说当主设备接收从设备发来的数据时它必须知道传送的数据量。
因为你可以在用户协议中传送这一信息,所以这也不会有什么问题。
例如我们可以使用表示字符串结束的“0”字节来表明没有数据需要传输了。
当SCL为“1”时SDA线上的数据是有效的。如:
SDA H -\ /---\ /---\ /---\
L \-----/ \---/ \--------/ \------....
SCL H ----\ /-\ /-\ /-\ /-\ /-\
L \---/ \-----/ \---/ \--/ \--/ \-....
| START | 1 | 1 | 0 | 1 | 0 |
使用这一协议的一个好处就是我们不需要精确的同步时钟信号。
当时钟信号有一点抖动时协议还可以工作。
严格地说,这一特性使得我们可以使用一个用户空间的程序来实现I2C协议,
而不需要内核驱动程序或者特定的硬件(如UART)。非常酷是吧?
计划
正如以前所述,我们不能使用PC的内部I2C总线,
但是我们可以使用任何外部接口来收发单独的数据比特流。
下面我们将使用我们PC上的RS232硬件接口。
换句话说我们仍然使用跟以前的文章中相同的通信接口,
不过我们省下了MAX232硬件和电容器等等。
当然最困难的部分是从头实现I2C协议,我花了五个周的时间学习和调试。
不过现在已经完成了,你可以直接COPY它:-)。我只希望你在使用这些代码的时候能记住它们的价值。
作为一个示范程序我们将制作一个温度计。当然你也可以测量其他的东西或者你只是用来开关电灯。
那就是你的自由了。
在下一篇文章中我们将增加一个本地的液晶显示屏。
也就是说你可以直接从液晶显示屏上读到温度而不用到你的Linux PC上去看了。
为了不使这篇文章过长我们将在下一篇中对液晶显示屏进行说明。
NTCs 体积小、便宜并且足够准确
|
The temperature sensor
你可以找到一些校准过的温度计(有一些是支持I2C协议的),
但它们都很贵。NTCs比较便宜,也非常好,甚至不需要手工校准。
如果你稍稍校准一下你就可以获得小数点后的精度。
NTCs有一个问题就是它们是非线性的。不过这只是一个半导体物理问题,
我们只需要找到合适的公式将非线性的曲线调整一下就行了。
微控制器本身就是一个小型计算机,做一些数学运算肯定没有问题。
T或Tc是我们需要得到的温度值。Rn是NTC在25'C的电阻值。你可以买
4K、7K等等的NTCs,Rn就是这些值。
电路
电路图,点击小图看大图。
|
现在我们已经备齐了制造一个数字温度计的所有器件。我们增加了两个NTC传感器。
一个测内部温度另一个测外部温度。如果需要你可以另行增加(比方说conn3和PC2是空闲的)。
我不希望你在下一篇文章中再做一套全新的电路,
所以在电路图中增加了连接液晶显示屏所需要的几条线。
上面还使用了一个发光二极管。它不贵而且对我们基本的调试确实很有用。
例如,我在开发PC和微控制器间的I2C通信协议时曾用它调试I2C状态机。
正常运行时我们可以让它闪烁,以表明温度计在工作。
因此,电路非常清晰明了。微控制器中的模数转换器用来测量NTC的电压并将其转换成温度值。
对于模数转换器使用何种参考电压,Atmega8有两个选项:5V(AVcc)或内部的2.56V。
在内部,我们不需要象外部那样大的温度范围的传感器。通常来说,+10'C到+40'C就足够了。
这样我们就可以在测量内部温度时使用2.56V参考电压。
由于1024个可能的数值仅仅被分配到0到2.56伏的电压区间上,
我们可以得到非常高的精度-2.5mV的分辨率(比大多数的数字伏特表都高)。
RS232的CD针脚是一条输入线,它连接到I2C总线的SDA线上。我们就是用它来从微控制器上读取数据。
DTR和RTS是输出线。当PC在SDA线上输出数据位时就控制DTR。
I2C主设备(在这里是linux PC)控制着SCL(时钟)线。
也就是说时钟线就是一条RS232的输出线。
78L05用来提供一个稳定的电源和参考电压。你可以使用任何7.5V到12V之间的交直流电源。
9V是一个很好的选择。
制作电路板
tuxgraphics.org出售所有本文中用到的元器件,并且还有一个腐蚀的很好的电路板。
|
当然你也可以重新使用以前的文章中用到的原始电路板,只需重新将LED连到pin-11上并增加所有的新器件。
如果你想要一个好看的电路的话你还是需要一个新的电路板。
因为电路非常复杂,恰当的腐蚀一个印刷电路板需要费很大的精力。
读了Iznogood在linuxfocus上关于gEDA的文章之后,我也打算使用gEDA来替代Eagle。
gEDA的概要绘图工具gschem非常好用。它不像Eagle那样有一个很大的元件符号库,
而我不得不为Atmega8创建这些符号。但它非常容易使用并且跟Eagle做的一样好。
稍微有点问题的就是绘制PCB的工具pcb。
当你刚从Eagle转过来时你首先会发现元器件可能从橡胶带上断开。
为了确信正确的橡胶带连接到正确的针脚上你必须经常运行
Connects->Optimize rats-nest 。你必须首先完成电路图才能制作电路板。
在这两者间只能手工添加注解。
我使用橙色图层来绘图。不知怎么搞的当打印时其他图层不能生成任何的输出。
问题是橙色图层实际上是在板子上有元器件的那一面。
如果你在这一层上增加文字,那么印在电路板上时你必须把它翻过来。
所以我只用pcb做基本的布局,其他的都用Gimp来做。
感谢shop.tuxgraphics.org ,
它们出售本文中提到的所有的器件。这样你就可以专注于其中有趣的部分并可以成功的装配电路了。
把所有器件组合在一起Putting everything together
当你装配电路时注意元器件的极性是非常重要的:电解电容器、
二极管、稳压二极管、78L05、LED和微控制器都是有极性的。
在你将微控制器插入插座之前你应该先检验电源部分。
如果电源工作不正常你不仅得不到正确的读数,而且有可能烧坏微控制器。
所以先外接一个电源(例如一个9V的电池)用电压表量一下,确信你在微控制器插座的针脚上得到恰好5V的电压。
下一步就把电路接到你Linux PC的RS232口上,
使用各种信号组合运行i2c_rs232_pintest程序。
i2c_rs232_pintest -d 1 -c 1
i2c_rs232_pintest -d 0 -c 1
i2c_rs232_pintest -d 1 -c 0
该程序用来设置RS232针脚RTS(用作SCL,使用-c选项)和DTR(用作SDA,使用-d选项)的电平值。
RS232口的电压大约为+/-10V。但在稳压二极管之后你应该只能测到-0.7伏(逻辑0)和+4-5V(逻辑1)。
只有通过上述测试之后才能插入微控制器。
使用I2C进行通信
下载(参见参考资料)linuxI2Ctemp.tar.gz文件并解压,I2C通信在下列两个文件中实现:
i2c_avr.c -- the i2c statemachine for the atmega8
i2c_m.c -- the complete i2c protocol on the linux side
我给了atmega8从地址3。你可以使用下面C函数给atmega8发送字符串“hello”:
address_slave(3,0); // tell the slave that we will send something
i2c_tx_string("hello");
i2cstop(); // release the i2c bus
on the microcontroller side you would receive this "hello" string with
i2c_get_received_data(rec_buf);
非常简单,从微控制器读数据也与此类似。
你可以看i2ctemp_avr_main.c中的温度读数是怎么做的。
温度是多少?
编译并从微控制器装载代码,直接在linuxI2Ctemp软件包目录中执行下列命令:
make
make load
编译下面两个程序i2c_rs232_pintest和i2ctemp_linux
make i2c_rs232_pintest
make i2ctemp_linux
或者你可以直接使用bin子目录中已经编译好的版本。
若读取温度,只需要运行:
i2ctemp_linux
它会打印出室内和室外的温度。因为I2C总线非常慢,
如果你想在网站上显示数据我建议不要直接在web服务器上运行i2ctemp_linux。
你可以在一个cron job中运行它,并将结果写到一个HTML文件中。
在linuxI2Ctemp包中的README文件包含一个示例脚本。
结论
I2C协议几乎不需要额外的硬件,并且已经为收发少量的数据做了优化。
如果我们想与自己的微控制器进行通信,那正是我们需要的。真是一个非常漂亮的解决方案。
在这篇文章中我主要集中在硬件部分上。如果你喜欢这篇文章我将另写一篇来介绍软件是如何工作的,
特别是如何来做模数转换和I2C协议是如何实现的。
下一篇文章我们也可以增加一个液晶显示屏以及华氏度和摄氏度之间的转换。
参考资料
对这篇文章发表评论
每篇文章都有各自的反馈页面。在这个页面里,您可以提交评论,也可以查看其他读者的评论:
2005-02-07, generated by lfparser version 2.51