用MAXQ3210构建温度记录仪
本篇应用笔记介绍如何使用低功耗微控制器MAXQ3210实现环境监测应用。增加一个通过一根1-Wire®总线实现供电和通信的数字温度传感器DS1822,我们可用最少的元器件构建一个电池供电的非易失温度记录系统。
可以下载相关演示代码。代码采用MAXQ汇编语言编写,在MAX-IDE开发环境自带的标准宏汇编预处理器和汇编器中编译。该代码是为MAXQ3210评估板编写,因此还需要以下器件(图1)。
- 温度传感器:DS1822经济型1-Wire数字温度传感器(TO-92封装)
- RS-232电平转换器:MAX233ACWP

图1. MAXQ3210 1-Wire温度记录仪演示电路所需的元器件
设计目标
演示代码要完成以下任务(图2):- 通过1-Wire网络(位模拟方式)与温度传感器DS1822通信。
- 每分钟唤醒一次测量温度。
- 将温度数据存储在MAXQ3210内部的非易失EEPROM中。
- 上电后以9600波特的速率通过位模拟串口发送温度记录数据。
- 在发送前将温度数据转换成容易识别的ASCII格式(十进制华氏度)。
- 根据主机要求清空存储器(擦除在EEPROM中存储的温度数据)。

图2. 温度记录应用的程序流程图
为何使用MAXQ3210?
几乎所有的低功耗MAXQ微控制器都可以实现这一应用,但MAXQ3210更适合用于温度记录。- 集成稳压器。MAXQ3210内部集成5V稳压器,可以直接由标准9V电池供电。MAXQ3210的5V稳压输出还可为其它设备供电(最大电流50mA)。这一特性非常重要,这意味着如果其它器件也可采用5V供电,则不再需要另加单独的电源芯片。
- 低功耗。MAXQ3210仅需消耗很小的电流,即使以3.58MHz全速运行,典型值也仅为6mA。当降低频率或处理器停止工作进入休眠状态时,电池电流还可更低。MAXQ3210内部集成的8kHz环形振荡器驱动一个长周期的唤醒时钟,可在长达2分钟的可编程间隔后将处理器从休眠状态唤醒。
- 内部数据EEPROM。在掉电时需要保存温度记录数据,这些数据可能要采集几小时,几天,甚至是几周时间。MAXQ3210数据存储空间有64个字的EEPROM,可非常容易的实现这一要求。EEPROM中的每个16位字都可调用Utility ROM中的一个函数单独修改;EEPROM技术意味着在写数据之前从来都不需要擦除操作。如果需要更多EEPROM空间,可将数据写入任何未用的程序EEPROM空间,该写入过程调用Utility ROM中的另一个函数以类似方式逐字修改,无需重载整个应用程序。
- 5V端口引脚。与所有MAXQ微控制器一样,MAXQ3210的端口引脚可灵活的设为输入、输出、弱上拉和三态。MAXQ3210还可提供多种接口选择。由于微控制器的端口为5V电平,可以直连5V器件或通过上拉电阻连接低功耗器件(工作在三态/开漏模式)。由于这一应用所需端口很少,使用大的微控制器会浪费许多功能。
- 压电扬声器驱动器。尽管压电扬声器功能在这一应用中没有使用,但在许多类型的环境监测应用中都需要产生可听见的告警声。例如烟雾监测和一氧化碳监测。MAXQ3210可直接驱动压电扬声器,可用非常简单的软件实现这一功能。仅需1位控制位来打开或关闭压电扬声器。根据所选的扬声器不同,MAXQ3210输出的幅度可以达到100dB。
- 小封装:MAXQ3210提供小型的24引脚TSSOP封装。
驱动1-Wire网络
Dallas Semiconductor/Maxim提供一系列使用1-Wire网络接口的传感器和其它器件。该接口的数据通信和供电仅需通过一根数据线再加一根地线,这意味着微控制器仅需一个端口即可与1-Wire传感器通信。1-Wire网络工作于一主多从模式(多点网络)。时序非常灵活,允许从机以高达16kbps的速率与主机通信。每个1-Wire器件都有一个全球唯一的64位ROM ID,允许1-Wire主机精确选择位于网络任何位置的一个从机进行通信。
1-Wire总线采用漏极开路模式工作,主机(或需要输出数据的从机)将数据线拉低到地表示数据0,将数据线释放为高表示数据1。这通常通过在数据线和VCC之间连一个分立电阻实现,但MAXQ3210的端口引脚支持弱上拉模式,只需将引脚切换到弱上拉模式,数据线即可浮高。因此MAXQ3210不需外接电阻。由于主机和从机仅需将数据线拉低,而从不将数据线主动拉高,因此数据线可以实现“线-或”功能,这可防止多个从机试图同时通过1-Wire总线发送数据时出现冲突。
为驱动1-Wire网络,MAXQ3210利用软件在一个引脚上实现以下类型的时隙。由于1-Wire所有时隙由主机启动,因此当MAXQ3210不与从机通信时不需要监测1-Wire线路。有关1-Wire时序的更多详细信息请参考DS1822的数据资料。
- Reset时隙宽度大约为1ms。在时隙的前半部分,主机(MAXQ3210)将1-Wire总线拉低,然后主机将总线释放,使其浮高。总线上的所有1-Wire从机复位,并在该时隙的后半段将总线拉低。这一步产生一个presence pulse (在线脉冲),向主机表明有一个或多个1-Wire从机在线,并且准备好开始通信。
- Write时隙大约长120µs,主机利用这一时隙向1-Wire从机发送0或1。两种写时隙都是以主机将总线拉低至少1微秒开始。如果发送1,主机随即释放1-Wire总线(使其浮高)。如果发送0,主机在该时隙剩余的时间内一直将总线拉低。
- Read时隙大约长60µs,主机利用这一时隙读取从机发送的0或1。该时隙是以主机将总线拉低至少1微秒开始的。随后主机将总线释放,允许从机将总线拉低(表示0),或将总线释放使其浮空为高(表示1)。主机在时隙中部采样总线读取从机发送来的数据。
#define OWIN M0[09h].6 ; PI1.6 #define OWOUT M0[01h].6 ; PO1.6 #define OWDIR M0[11h].6 ; PD1.6;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Function : Reset1Wire ;; Description : Sends a standard speed 1-Wire reset pulse on P1.6 ;; and checks for a presence pulse reply. ;; Inputs : None ;; Outputs : C - Cleared on success; set on error (no presence ;; pulse detected) ;; Destroys : PSF, LC[0] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Reset1Wire:move OWDIR, #1 ; Output modemove OWOUT, #0 ; Drive lowmove LC[0], #RESET_LOWdjnz LC[0], $move OWOUT, #1 ; Snap highmove LC[0], #SNAPdjnz LC[0], $move OWDIR, #0 ; Change to weak pullup inputmove LC[0], #RESET_PRESAMPLEdjnz LC[0], $move C, OWIN ; Check for presence detectmove LC[0], #RESET_POSTSAMPLEdjnz LC[0], $ret;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Function : Write1Wire ;; Description : Writes a standard speed 1-Wire output byte on P1.6. ;; Inputs : GRL - Byte to write to 1-Wire. ;; Outputs : None. ;; Destroys : PSF, AP, APC, A[0], LC[0], LC[1] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Write1Wire:move APC, #080h ; Standard mode, select A[0] as Accmove Acc, GRLmove OWDIR, #1 ; Output drive modemove LC[1], #8 ; 8 bits to writeWrite1Wire_slot:move OWOUT, #0 ; Drive low for start of write slotmove LC[0], #WRITE_PREBITdjnz LC[0], $rrc ; Get the next bitjump C, Write1Wire_one Write1Wire_zero:move OWOUT, #0 ; Keep the line low (zero bit)jump Write1Wire_next Write1Wire_one:move OWOUT, #1 Write1Wire_next:move LC[0], #WRITE_POSTBITdjnz LC[0], $ ; Finish the time slotmove OWOUT, #1 ; Drive back high (end of slot)move LC[0], #WRITE_RECOVERYdjnz LC[0], $ ; Recovery time perioddjnz LC[1], Write1Wire_slotret
实现读时隙的功能与之类似。注意,在1-Wire总线上所有数据均为低有效位(LSB)先发。
利用MAXQ3210实现1-Wire时序时,另一点需要注意的是:尽管1-Wire总线上拉电阻的阻值与总线上的设备数有关,但通常在4kΩ到5kΩ之间。可是MAXQ3210端口引脚上的弱上拉电阻为50kΩ到100kΩ。为了防止1-Wire总线从低电平到高电平转换的时间过长,演示代码先将P1.6输出设为短暂的高电平,将总线强制拉高,然后变为正常的弱上拉模式。只要该过程不是在从机试图将总线拉低的时候进行,就不会出现问题。另外,还可以在总线上再加一个分立的上拉电阻,这样就可以正常的方式使端口输出低代表0,输出三态代表1。
注意:当构建的1-Wire网络传输距离较远或连接的从机数量较多时,还需要注意其他事项。
用DS1822测量温度
尽管MAXQ3210可以使用上面的代码与大多数1-Wire从机器件通信,在本应用中我们将主要考虑与DS1822通信。DS1822是一个1-Wire从机器件,可实现9到12位的摄氏温度测量,测量结果可被1-Wire主机读取。与多数1-Wire从机一样,DS1822可以完全由1-Wire总线供电,我们称之为寄生供电。DS1822的测量范围可达-55°C至+125°C,适用于多数的室内外温度测量应用。温度分辨率在9位下为0.5°C,12位下0.0625°C。进行一次温度转换所需时间在低分辨率下约为94ms,在最高分辨率下约为750ms。由于这是一个简单应用,我们选择9位分辨率,并忽略最低位(0.5°C)。这样就可使整个8位带符号温度数据与MAXQ3210的8位累加器匹配。
所有的1-Wire从机器件都支持一个通用指令集,从而使得主机可以判断总线上的从机数目,读取ROM ID,并且可以与某一个从机或一组从机进行通信。一旦某个1-Wire从机被激活,主机可以针对该从机类型向其发送特殊指令。其它所有未被激活的从机均处于等待状态,直到下一个复位脉冲出现,才开始再次监测1-Wire总线。
由于在我们的应用中总线上仅有一个1-Wire器件,我们可以使用最简单的指令集访问从机器件,不需要读取从机的ROM ID。当总线上有多个从机器件时,ROM ID被用来区分不同的从机器件。我们的程序中也读取了一次DS1822的ROM ID,但仅是为了演示。
我们将使用下面的1-Wire指令集,DS1822支持的其它指令请参考其数据资料。
- Read ROM [33h]。这一指令假设1-Wire总线上只有一个从机器件。1-Wire 从机收到该指令后将其8字节的ROM ID发回1-Wire主机。这个ID包括48位序列号,8位CRC,8位家族码。家族码代表器件类型。DS1822的家族码为22h。收到Read ROM指令后,1-Wire从机被激活,并响应后续与该从机器件相关的指令。
- Skip ROM [CCh]。1-Wire总线上有一个或多个从机器件时都可以使用这一指令。这条指令激活总线上的所有从机,与从机的ROM ID无关。当总线上只有一个从机时,可利用这条指令不读取从机ID而激活从机,使其接收后续相关指令。当总线上有多个从机时,如果使用这条指令,则必须保证后面的指令不会造成从机向主机发送数据。因为从机可能发送不同的数据而造成数据冲突。
- Write Scratchpad [4Eh]。这是DS1822专用的指令,之前先用Read ROM 或Skip ROM指令激活器件。在该指令后1-Wire主机发送3字节的配置数据用来配置DS1822,包括温度转换的位分辨率。更多详情请参考DS1822的数据资料。
- Read Scratchpad [BEh]。这也是DS1822专用的指令,该指令允许主机从DS1822读取最多9字节数据。这些数据包括通过Write Scratchpad指令设置的配置寄存器值,以及最近的温度转换结果。更多详情请参考DS1822的数据资料。我们的应用仅需要最开始的两个字节,这两个字节就是最近的温度转换结果。
- Convert Temperature [44h]。这是DS1822专用的指令。DS1822收到该指令后开始测量温度,并将其按指定位分辨率转换成数字量。结果存储到两个内部寄存器中,1-Wire主机可以通过Read Scratchpad读取。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Function : ConvertAndReadTemp ;; Description : Sends commands to measure temperature and read ;; scratchpad from the DS1822. ;; Inputs : None. ;; Outputs : GRL - 8-bit signed temperature value, in degrees C. ;; Destroys : PSF, AP, APC, A[0], A[1], A[2], LC[0], LC[1] ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ConvertAndReadTemp:call Reset1Wire ; Reset the DS1822move GRL, #OW_SKIP_ROM ; Select the DS1822call Write1Wiremove GRL, #OW_CONVERT ; Send temp convert commandcall Write1Wiremove OWDIR, #1 ; Turn on strong pullup for draw currentmove OWOUT, #1move LC[0], #55 ; About a second delay:move LC[1], #0djnz LC[1], $djnz LC[0], delaycall Reset1Wire ; Conversion completed; reset againmove GRL, #OW_SKIP_ROM ; Select againcall Write1Wiremove GRL, #OW_RD_SCRATCH ; Read the scratchpad valuescall Write1Wirecall Read1Wiremove A[1], GRL ; Temp LSB 3210xxxxcall Read1Wiremove A[2], GRL ; Temp MSB sssss654move Acc, A[1] ; 3210xxxxand #0F0h ; 3210----xchn ; ----3210move A[1], Accmove Acc, A[2] ; sssss654and #00Fh ; ----s654xchn ; s654----or A[1] ; s6543210move GRL, Accret
将测量结果存储在数据EEPROM中
为防止1-Wire总线偶然出现数据错误,演示代码每次测量都执行三次温度转换(A,B和C),并从中选择一个结果存储,选择的依据为:- 如果所有数据相同,则存储该数据。
- 如果三个中有两个数据相同(A = B,B = C或A = C),则选择相同的数据存储。
- 如果没有数据相同,则取中间值存储。例如,如果(A B C),则存储B。
;; Two out of three majority vote, or failing that, the measurement ;; in the middle of the three.move Acc, A[4] cmp A[5]jump E, recordTempA ; If (A==B), use that valuecmp A[6]jump E, recordTempA ; If (A==C), use that valuemove Acc, A[5]cmp A[6] jump E, recordTempB ; If (B==C), use that valuemove Acc, A[4]sub A[5]jump S, B_greaterThan_A ; Sign is set if (A-B) is negative;; If (A B) { ;; If (C A) record A (C A B) ;; If (B C) record B, (A B C) ;; else record C (A C B)A_greaterThan_B:move Acc, A[4]sub A[6] ; A-Cjump S, recordTempA ; Sign is set if (A-C) is negativemove Acc, A[5]sub A[6] ; B-Cjump S, recordTempC ; Sign is set if (B-C) is negativejump recordTempB;; If (B A) { ;; If (C B) record B (C B A) ;; If (A C) record B, (A B C) ;; else record C (B C A)B_greaterThan_A:move Acc, A[5]sub A[6] ; B-Cjump S, recordTempB ; Sign is set if (B-C) is negativemove Acc, A[4]sub A[6] ; A-Cjump S, recordTempC ; Sign is set if (A-C) is negativejump recordTempBrecordTempA:move GRL, A[4]jump recordTemprecordTempB:move GRL, A[5]jump recordTemprecordTempC:move GRL, A[6]jump recordTemprecordTemp:move A[15], GRLmove GRL, #'@'call TxCharBBmove GR, DP[0]move GRL, GRHcall TxHexByteBBmove GRL, DP[0]call TxHexByteBBmove GRL, #' 'call TxCharBBmove GRL, #'W'call TxCharBBmove GRL, A[15]call TxHexByteBBmove GRL, A[15] ; Low byte contains temp datamove GRH, #055h ; High byte marks nonzero entrylcall UROM_loadData ; Write entry to data EEPROMcall IncDP0_EE ; Move to the next entry positionmove GR, #0000h ; Erase any data that existslcall UROM_loadData ; Erase the oldest entry
评论