第二部分 -- 一个数字温度计或跟你的atmel微控制器进行I2C通信
ArticleCategory: [Choose a category, do not translate
this]
Hardware
AuthorImage:[Here we need a little image from you]
TranslationInfo:[Author + translation history. mailto: or
http://homepage]
original in en Guido
Socher
en to zh SEVEN
AboutTheAuthor:[A small biography about the author]
Guido喜欢Linux,因为对于开发自己的硬件来说Linux确实是一个非常好的系统。
Abstract:[Here you write a little summary]
在第二部分中我们将连接一个液晶显示屏(LCD),并且我将介绍一下其软件是如何工作的。
第一次读这一系列文章的读者请先阅读
第一部分(February2005
article365).
ArticleIllustration:[This is the title picture for your
article]
ArticleBody:[The article body]
新东西
在
上一篇文章
中我们已经制做好了大部分的硬件和温度计的主要功能部分,也做好了到Linux PC
的数据传输。在这一篇中我们将增加一个液晶显示屏和一个非常简单的gtk GUI界面。
这两项工作都很容易。所以文章的其他部分我将解释一下I2C软件和模数转换器是如何工作的。
液晶显示屏
我们使用一个HD44780兼容LCD显示屏,因为我们曾在以前的文章中使用过它。
由于你可以给它们发送ASCII字符,所以这些显示屏与微控制器的组合也很容易使用。
像所有这一系列的文章一样,你可以在
shop.tuxgraphics.org上找到包括LCD
在内的所有元器件。
与以前的所有文章一样中我使用相同的驱动代码。
实现LCD驱动的文件是lcd.c、lcd.h和lcd_hw.h。
它们放在一个包中,你可以在本文的最后找到并下载。
这些代码的接口的确非常容易使用:
// call this once:
// initialize LCD display, cursor off
lcd_init(LCD_DISP_ON);
// to write some text we first clear
// the display:
lcd_clrscr();
lcd_puts("Hello");
// go to the second line:
lcd_gotoxy(0,1);
lcd_puts("LCD");
the linuxfocus
September2002 article "Understanding HD44780 compatible LCD-displays"
中讲述了HD44780显示屏是如何工作的。
我们的软件在16x2和20x2的LCD上都能工作。
这里我们对电路图进行了一些更新。我发现有一些LCD与其他的比较起来有更高的电容负载和更低的电阻。
这可能因为它们有更好的ESD保护。当把LCD显示屏连接到SCK或MOSI针脚时,
这一额外的负载在内部电路编程中可能会引起误码。
第一个解决方案是我在与LCD的连接线上连接了额外的电阻。
它在我这里正常工作可是其他的人,尤其是笔记本电脑用户仍然会有问题。
为了彻底避免这一问题我更新了电路图,现在把LCD显示屏的D7和RS针连接到了PD7和PD6上。
即使你已经做好电路板,做这些改变也不会有什么问题的。
你只要在板子下面加一点电线并将到PB3的连接用小刀切断就可以了。
一个小的GUI界面
为了方便喜欢桌面系统上GUI界面的用户我做了一个非常简单的GUI。
它只有两个标签组成,它们用来显示i2ctemp_linux命令的两条线的输出
(i2ctemp_linux是通过I2C总线从电路上读取温度的命令)。
现在我们就有了一个非常酷的温度计了。它有以下一些功能:
-
你可以在本地从液晶显示屏上读取温度
-
你可以在你桌面上放一个GUI程序
-
你可以使用一个cronjob把数值写到一个log文件中进行长期的统计
现在我将在本文剩下的部分解释一下软件内部的一些东西。
模数转换是如何工作的
Atmega8支持两种模式:一种模式下它永远测量模拟信号并且在测量就绪时仅触发一个中断。
应用程序软件就可以在中断发生时快速地将结果从两个寄存器拷贝到一个变量中。
另一种模式被称为单采样模式。在这里仅做一次转换。单采样模式速度很快。
包括寄存器数据在读出前的建立时间,你仍然可以获得每秒100次的转换速度。
这对我们来说绝对足够了。所以我们采用这种模式。
在我们的Atmega8上我们可以使用模拟输入端ADC0和ADC3。
除此之外还有AGND(模拟地,连接到普通地)、AREF(参考电压)和AVCC(连接+5V)。
在模数转换过程中模拟信号与AREF相比较。与AREF相等的模拟信号相当于数字值1023。
AREF可以是任何外部0-5V之间的参考电压。如果不使用外部参考电压,
你也可以通过使用一个内部参考电压(2.56V)或AVCC来做精确的转换。
具体使用哪一种是在软件中通过ADMUX寄存器的REFS0和REFS1来决定的。
模数转换器一次可以转换ADC0-ADC3间的一条输入线。
进行转换之前你必须通过设置ADMUX寄存器的标志位来通知芯片使用哪一个通道。
这样,一个简单的模数转换器看起来就像这个样子:
volatile static int analog_result;
volatile static unsigned char analog_busy;
analog_busy=1; // busy mark the ADC function
channel=0; // measure ADC0
// use internal 2.56V ref
outp((1<<REFS1)|(1<<REFS0)|(channel & 0x07),ADMUX);
outp((1<<ADEN)|(1<<ADIE)|(1<<ADIF)|(1<<ADPS2),ADCSR);
sbi(ADCSR,ADSC); // start conversion
现在微控制器就将进行模数转换,并且一旦它准备好,它就会调用SIGNAL(SIG_ADC)函数。
在这一函数中我们可以将结果拷贝到一个变量。由于微控制器有一些锁机制来模拟“原子性”读,
作为一个程序员你必须首先监视你所读到的低8位数据。
SIGNAL(SIG_ADC) {
unsigned char adlow,adhigh;
adlow=inp(ADCL); /* read low first, two lines. Do not combine
the two lines into one C statement */
adhigh=inp(ADCH);
analog_result=(adhigh<<8)|(adlow & 0xFF);
analog_busy=0;
}
在这之后我们就可以从analog_result变量中获得一个整数做为可用的模数转换的结果。
这个数可以在程序的其他任何地方使用。非常容易使用。
由于所有的中断都需要调用sei();函数。为了全局可见,它应该在主程序的某个地方实现
(上面没列出)。
我简单介绍一下一些比特位和标志位:
-
ADEN: 模数转换使能,在设置ADSC前设置该位
-
ADIE: 允许ADC中断(=允许调用SIGNAL(SIG_ADC))
-
ADIF: ADC中断标志(转换前必须置1)
-
ADPS: ADC时钟预调节比特:
必须设置为--使得当时钟频率被预调节因子除之后,得到一个50到200KHz之间的值。
其中除法因子为2ADPS。上述设置(ADPS2=1, ADPS1=0,
ADPS0=0,二进制100=十进制4,而24=16,所以除法因子为16)对1MHz的时钟频率很合适。
Atmega8有几种可选的参考电压。参考电压会与我们的模拟输入电压相比较。
它相当于数字电压值1023。
REFS0=0, REFS1=0 | 使用外部的AREF,内部Vref被关闭 |
REFS0=0, REFS1=1 | 使用AVCC,AREF针脚上可选接外部电容 |
REFS0=1, REFS1=1 | 使用内部2.56V参考电压,AREF上可选接外部电容 |
AREF上一个可选的电容器可用来抑制噪声和稳定AREF电压。
它是如何工作的:I2C通信,Atmega8部分
我已经在第一部分
(February2005 article365)
讲述了I2C协议是如何工作的。现在让我们来看一看软件部分。Atmega8有对I2C通信的硬件支持。
所以你没有必要真正的去实现这一协议。你只需要实现一个状态机。
这一状态机可以告诉Atmega8下一步要做什么。请看下面这个例子:
当Atmega8收到一个等于它自己(从)地址的I2C包时,它就会使用状态码0x60
(对于其他的事件我们使用其他的状态码)来调用SIGNAL(SIG_2WIRE_SERIAL)函数。
-->
现在我们必须通过设置几个寄存器来告诉Atmega8下一步要做什么。
在这里我们就要告诉它:接收数据包并回送证实信号。
当真正的数据被接收以后我们就会被以状态码0x80调用。
-->
然后我们就可以读取数据字节并且告诉Atmega8如果下一个字节的数据到来时继续回送证实信号。
通信结束时我们会得到状态码0xA0(停止条件),然后我们就可以通知我们的应用程序一条完整的消息已经收到了。
I2C从模式全部的状态机和所有可能的状态值在Atmega8手册(
见本文后面的参考区)第183页的数据表中有详细的描述。
发送数据也与此类似,看一看代码吧。
它是如何工作的:I2C通信,Linux侧
首先说一下硬件。虽然我们使用了I2C总线,但我们仅仅通过它来使用一个从设备和主设备(Linux PC)
间的点到点的连接。所以虽然从设备要下拉线上的电压,但只要它不至于导致短路,
我们就可以节约一个上拉电阻。在这里我们只在线上接入一个4.7K的电阻。
电平值必须调节。这可以使用一个稳压二极管将负电压限制到-0.7V,
而将正电压限制到最大+5.1伏。
在对Atmega8的内部有了更多的了解之后我得出一个结论:
由于通过4.7K电阻的电流非常小,Atmega8输入级内部的保护可能已经足够了。
实际上我们并不是必须使用稳压二极管。没有稳压二极管也不会有什么影响。
Linux I2C软件基本上实现了一个完全的I2C协议栈。这是因为我想要一个小的命令行工具,
而这一工具应该不依赖于任何特殊的库或内核模块而独立工作。
如果你看一下i2c_m.c文件(见下载部分),你就可以看出每一条I2C消息都是一个比特一个比特的构造的。
为了产生比特位,我们必须控制RC232接口的物理针脚。
这是用ioctl调用来实现的:
// set RTS pin:
int arg=TIOCM_RTS;
ioctl(fd, TIOCMBIS, &arg);
或者产生一个0:
// clear RTS pin:
int arg=TIOCM_RTS;
ioctl(fd, TIOCMBIC, &arg);
如果你想将这一协议栈移植到其它不同的OS上,你只需改动这些行就可以了。
其他的都是普通的C语言。
USB到RS232
对于当今那些没有RS232接口的笔记本电脑来用户来说,你可以简单地使用USB-RS232转换器。
例如我使用了一个没有名字的转换器,它带有一个Prolific 2303芯片。
该转换器在/proc/bus/usb/devices文件中看起来如下:
Vendor=067b ProdID=2303 Rev= 2.02。
你可以查看:
"Use your ATEN
UC-232A USB adapter with Linux (Linuxfocus, November 2001, article 223)".
结论
现在我已经使用这一温度计2个月了,我非常喜欢它。因为你可以直接从液晶显示屏上读取温度。
并且你也可以将所有的数据存储到你的PC上。你可以直接看,也可以统计绘图。非常酷。
室外的传感器必须进行适当的保护以避免日晒雨淋。你可以将它包在一些塑料制品中,
但我不推荐这样做。无论你捆得多么紧,水最终总会流进去的,并会存在里面。
NTC非常抗燥,如果湿一点是没什么关系的,只要还能干就行。
使用一个倒装的(口朝下)管子,这样水就可以流出来了。
你仍然可以从
shop.tuxgraphics.org
在线商店定购所有的元器件(LCD显示屏,印刷电路板,微控制器等)。
Have fun!
参考资料