找回密码
 立即注册

QQ登录

只需一步,快速开始

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

HMC5883数字指南针(电子罗盘)的设计论文下载 带单片机源程序

  [复制链接]
跳转到指定楼层
楼主
数字指南针(电子罗盘)的设计
大学本科生毕业设计(论文)
目 录
摘要……………………………………………………………………………………………………1
前言……………………………………………………………………………………………………3
第一章 绪论……………………………………………………………………………………….....4
第1.1节 电子罗盘的原理…………………………………………………………………………4
第1.2节 电子罗盘的发展趋势……………………………………………………………………4
第二章 总体方案设计…………………………………………………………………………….....6
第2.1节 总体方案选择…………………………………………………………………………....6
第2.2节 硬件方案………………………………………………………………………………....7
第2.3节 软件方案………………………………………………………………………………....8
第三章 硬件系统设计…………………………………………………………………………….....10
第3.1节 控制模块设计…………………………………………………………………………....10
第3.2节 传感器模块设计………………………………………………………………………....11
第3.3节 显示模块设计……………………………………………………………………………13
第3.4节 总体电路设计…………………………………………………………………………....14
第四章 软件系统设计……………………………………………………………………………….17
第4.1节 主程序设计………………………………………………………………………………17
第4.2节 传感器模块程序设计……………………………………………………………………18
第4.3节 显示模块程序设计………………………………………………………………………19
第五章 系统调试…………………………………………………………………………………….20
总结……………………………………………………………………………………………….......21
参考文献………………………………………………………………………………………….......22
致谢……………………………………………………………………………………………….......23
附录1 实物照片说明………………………………………………………………………………..24
附录2 部分源程序…………………………………………………………………………………..24

【摘要】:当今社会交通越来越发达,导航系统也逐渐普遍。在以前人们使用纸质地图看路况。现如今由于经济发展,交通路线也变化非常之大。现在虽然有GPS,但是在一些山区有覆盖遮蔽的地方,GPS也会失去作用。汽车出行不方便,为解决这个问题,本文主要研究使用在汽车导航设备的能够精确定向的电子罗盘系统。本文主要介绍磁阻式电子罗盘的工作原理,并详细介绍了磁阻传感器HMC5883,AT89S52单片机的磁阻式电子罗盘的硬件设计;根据传感器信号输出特点,利用AT89S52单片机处理信息功能经过分析后,经显示屏显示方向。在研制磁阻电子罗盘原理样机过程中,我对系统做出一定的误差补偿,使其系统的精度提高,并且对系统做经一步的改进。

对研制的数字电子罗盘系统样机,进行测试。其结果显示,该数字电子罗盘能对行驶方向进行的动态测量,在 0°到360°范围行驶方向测量精度可达±2.0°。研制的数字电子罗盘,具有体积小、性能稳定、反应快、低功耗等优点,在车辆导航设备领域有非常广阔的应用前景。

前言
通常的导航仪器主要有两种:陀螺罗经和磁罗盘。对地磁场测量方向的仪器叫做磁罗盘。我国发明指南针就是一个简易的磁罗盘,对整个人类社会发展做出巨大贡献。在公元 50 年左右,磁石已经被运用到导航航啦,并且研制出了司南。在公元 960-1127 年时候,支撑是的指南针——指南龟被研制出来。到 20 世纪初,随着工业的发展,罗盘制造工艺也得到了飞速的发展,材料的选择和机械制造使得罗盘的性能有了显著地提高。尤其是是机械式磁罗盘,现在某些情况下依然使用机械式磁罗盘 。到了20世纪出,陀螺罗盘的问世,对罗盘又是一场革命。罗盘感应这地球的自转,磁性物质对其没有影响。使得陀螺罗盘的标度盘非常稳定,读取数据更加精确。当代GPS虽然有广泛的应用,但是信号经常被物体所遮挡,使其精度大打折扣。有效性也大大降低。数字电子罗盘系统则将填补这一个不足,采用地磁场的工作原理,无论何时何地磁场的水平分量永远指向地磁北极,对GPS信号进行有效补偿。
随着科技发展和道路建设完善,汽车会给人们生活极大方便,汽车将会普及在我们生活中。电子罗盘定向系统将会出现每一辆汽车里;届时很多人会开自己的车旅游,回家,谈生意等等,当置于一个陌生的环境中,导航定向对于行车安全非常重要。所以,迫切需要研究出一种低功耗,便于携带,内置磁场感应器,系统稳定,并且能完成精确定向的微系统,而本课题设计就是研究出一个数字电子罗盘,专门解决这个问题而产生的。

第一章 绪论第1.1节 电子罗盘原理
目前电子罗盘按照有无倾角补偿可以分为平面电子罗盘和三维电子罗盘,也可以按照传感器的不同分为磁阻效应传感器、霍尔效应传感器和磁通门传感器。
利用磁性材料的磁阻效应制成磁性效应传感器。磁性材料的易磁化轴、形状和磁化磁场的方向影响着其磁化方向。图 1.1显示出,当电流流通磁性材料时,其电阻阻值大小由材料流通电流的方向与磁化方向的夹角θ决定。把磁场 M加在磁性材料上,之前磁化方向开始转动。如果磁化方向转向与电流的方向垂直,θ角增大,电阻阻值将减小;如果平行,即θ角减小,电阻阻值将增大,电流方向与电阻值的关系为cos2θ,这就是磁阻效应                                                                           

图1.1磁阻效应原理

磁阻式传感器具有低功耗,抗干扰,温度稳定性好,而且电路很容易搭建。灵敏度和线性度比较好。其性能及稳定性容易被迟滞误差和零点温度漂移所影响,地磁场强度比较小,外界非磁场容易对磁阻式电子罗盘产生干扰。电子罗盘发展相当迅速,在需要导航的系统的各行各业。并且有着非常大的应用前景。
第1.2节 电子罗盘的发展趋势
由于GPS导航在高山、树林时信号传输不能很好的回馈到卫星中。同时GPS容易受到其他信号、波形干扰,导致其稳定在某些地区较差。所以需要一个稳定的导航系统在任何场地都能测出行驶方向,所以这个导航系统有很大的发展前景。有地磁大小和方向随地点不同而不同, 无论在地球的每一个地方,磁场的水平分量永远指向磁北,电子罗盘根据这一个原理制作的,所以电子罗盘可以用于稳定的精确的汽车导航定向,电子罗盘系统的市场需求也在我国日趋明显,而且也初具规模。我认为未来电子罗盘的发展的方向有以下几点:
(1)使电子罗盘导航系统科技含量更高,整个制造流程可以形成一个完整的产物链
(2)把GPS的技术和电子罗盘技术相结合,对于导航的精确性、实用性和稳定性有提高。
(3)把电子罗盘做成信息技术服务的产业,使其应用到更多的行业里,加快电子罗盘研究与发展。
未来科技发展更加快速,相对磁场技术也会越来成熟,电子罗盘系统将朝着先进性、经济性、实用性、功能型的成熟完善的系统!

第二章 总体方案设计
第2.1节 总体方案选择
对于电子罗盘的设计,经查找资料结合所学知识,我得到两种方案。
方案一:采用Philips公司生产的KMZ52感应磁场传感器
KMZ52是Philips公司生产的一种磁阻传感器,是利用坡莫合金薄片的磁阻效应测量磁场的高灵敏度磁阻传感器。该磁阻传感器内置两个正交磁敏电阻桥、完整的补偿线圈和设置/复位线圈。补偿线圈的输出与当前测量结果形成闭环反馈,使传感器的灵敏度不受地域限制。这种磁阻传感器主要应用于导航、通用地磁测量和交通检测。该磁阻传感器在金属铝的表面沉积了一定厚度的高磁导率的坡莫合金,在翻转线圈和外界磁场两个力的作用下,电子改变运动方向,使得磁敏电阻的阻值发生变化。同时KMZ52的斑马条电阻成45°放置,这使得电子在正反向磁场力作用下有较好的对称性。由于加入了翻转磁场,KMZ52的变化曲线与普通的磁敏电阻不同,更加线性化。KMZ52磁阻传感器的核心部分是惠斯通电桥,是由4个磁敏感元件组成的磁阻桥臂。磁敏感元件由长而薄的坡莫合金薄膜制成。在外加磁场的作用下,磁阻的变化引起输出电压的变化。

图2.1 KMZ52传感器引脚图

方案二:使用霍尼韦尔HMC5883L各向异性磁阻传感电路
霍尼韦尔 HMC5883L 是一种表面贴装的高集成模块,并带有数字接口的弱磁传感器芯片,应用于低成本罗盘和磁场检测领域。HMC5883L 包括最先进的高分辨率HMC118X系列磁阻传感器,并附带霍尼韦尔专利的集成电路包括放大器、自动消磁驱动器、偏差校准、能使罗盘精度控制在1°-2°的12位模数转换器。简易的I2C 系列总线接口。HMC5883L 是采用无铅表面封装技术,带有16 引脚,尺寸为3.0×3.0×0.9mm。HMC5883L 的所应用领域有手机、笔记本电脑、消费类电子、汽车导航系统和个人导航系统。HMC5883L采用霍尼韦尔各向异性磁阻(AMR)技术,该技术领先于其他磁传感器技术。这些各向异性传感器具有在轴向高灵敏度和线性高精度的特点.传感器具有的对正交轴的低灵敏度的固相结构能用于测量地球磁场的方向和大小,其测量范围从毫高斯到8高斯(gauss)。霍尼韦尔的磁传感器在低磁场传感器行业中是灵敏度最高和可靠性最好的传感器。

图2.2 HMC5883L传感器引脚图

通过对比各传感器特点我们了解到它们的优缺点,HMC5883L三轴磁阻传感器和ASIC都被封装在一起了,不需要外接ASIC,而12-bit ADC与低干扰AMR传感器,能在±8高斯的磁场中实现2毫高斯的分辨率,且内置驱动器,显得更为优越。霍尼韦尔的磁传感器在低磁场传感器行业中是灵敏度最高和可靠性最好的传感器。
综上结合所学知识我选择传感器方案二,使用霍尼韦尔HMC5883L各向异性磁阻传感电路。

第2.2节 硬件方案
数字电子罗盘3大模块分别是:传感器模块、数据采集模块和MCU模块。需要硬件有: 磁阻传感器、 双轴加速度传感器、AD转换器以及单片机的磁阻式电子罗盘。硬件总体框图如下:
图2.3系统硬件总体框图
采用三轴磁阻传感器进行地球磁场矢量测量,双轴加速传感器可以传感地球重力场中测量载体的姿态,然后通过姿态坐标变换将磁阻传感器得到载体坐标的测量信号变换到地平坐标系。其磁阻式传感器HMC5883包含输出为3路的差分模拟电压值,差分值大约为几毫伏,信号经过传感器内置的ASIC放大器把信号进行放大,再进行模数转换器进行放大和模数转换;再由有单片机处理数字信号,经由处理后得到航向角由显示屏来显示;复位电路用于恢复磁阻传感器在强磁干扰后的灵敏度;电源模块分别为复位电路和信号处理电路供电。
第2.3节 软件方案
系统软件除了完成初始化、信号采集、信号调理、A/D转换,再到单片机中进行信号处理分析,然后判断能否输出。其框图如下所示:
图2.4 软件总体框图
单片机对传感器失调、温度漂移等干扰造成的误差进行调整。失调和温度漂移会在传感器敏感信号上面叠加一个直流偏置电路,单片机通过将传感器在置位和复位情况下得到的信号进行分析,算出平均值。就可以得到由于失调和漂移造成的直流偏置信号,在方向角计算前对该偏置信号进行补偿即可消除其影响。再判断能否输出,如果不能输出,则再采集一次;能输出则把数据送到显示屏显示。

第三章 硬件系统设计

第3.1节 控制模块设计
AT89S52是一种低功耗、高性能CMOS 8位微控制器,具有8K 在系统可编程Flash存储器。使用Atmel 公司高密度非易失性存储器技术制造,与工业80C51 产品指令和引脚完全兼容。片上Flash允许程序存储器在系统可编程,亦适于常规编程器。在单芯片上,拥有灵巧的8 位CPU 和在系统可编程Flash,使得AT89S52在众多嵌入式控制应用系统中得到广泛应用。
AT89S52具有以下标准功能: 8k字节Flash,256字节RAM, 32 位I/O 口线,看门狗定时器,2 个数据指针,三个16 位 定时器/计数器,一个6向量2级中断结构,全双工串行口, 片内晶振及时钟电路。另外,AT89S52 可降至0Hz 静态逻 辑操作,支持2种软件可选择节电模式。空闲模式下,CPU 停止工作,允许RAM、定时器/计数器、串口、中断继续工 作。掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止,直到下一个中断或硬件复位为止。

图3.1 AT89S52引脚图 DIP封装

图3.2 AT89S52电路连接图

第3.2节 传感器模块设计
1.工作原理
霍尼韦尔HMC5883L磁阻传感器电路是三轴传感器并应用特殊辅助电路来测量磁场。通过施加供电电源,传感器可以将量测轴方向上的任何入射磁场转变成一种差分电压输出。磁阻传感器是由一个镍铁(坡莫合金)薄膜放置在硅片上,并构成一个带式电阻元件。在磁场存在的情况下,桥式电阻元件的变化将引起跨电桥输出电压的相应变化。这些磁阻元件两两对齐,形成一个共同的感应轴,随着磁场在感应方向上不断增强,电压也会正向增长。因为输出只与沿轴方向上的磁阻元件成比例,其他磁阻电桥也放置在正交方向上,就能精密测量其他方向的磁场强度。
2.电源管理
该器件可有两种不同的供电模式。第一个是内部运作的VDD供电电源,第二个是为IO接口供电的VDDIO电源,当然VDDIO的电压可以与VDD电源相近;单电源模式,或在VDDIO 电压低于VDD 的情况下,HMC5883L都能正常运作并能与其他装置兼容。

图3.3 HMC5883L内部示意图

3.I2C接口
控制该装置可以通过I2C总线来实现。该装置将作为从机在一个主机(例如:处理器)的控制下连接总线。该装置必须符合I2C-Bus SpecificationI2C-总线技术规格标准),作为一个I2C 兼容装置,该装置包含一个7-bit串行地址,并支持I2C 协议。这一装置可以支持标准和快速模式,分别为100kHz和400kHz,但不支持高速模式(Hs)。还需要外接电阻才能支持这些标准和快速模式。要求主机的活动(寄存器的读取和写入)优先于内部活动,例如:测量。这一优先次序的安排是为了不让主机等待,同时I2C总线占用的时间比必需的时间长。
4.置位/复位带驱动的H-桥式电路
ASIC包含大型开关FETs,可以传输大而短的脉冲到传感器的置位/复位带。这一置位/复位带在很大程度上是一种电阻性负载。并不需要外部去增加外部置位/复位回路。每次测量时,ASIC会自动完成置位/复位。首先一次置位脉冲产生后进行测量,然后,一次复位脉冲产生后进行测量,两次测量的差值的一半将会被放置在三轴上每根轴的数据输出寄存器上。这样,在所有测量中传感器的内部偏差和温度漂移差值就可以被移除/抵消了。
5.寄存器访问
下面表格列出了寄存器及其访问。所有地址为8 bits。
表3.1 寄存器列表
地址
名称
访问
00
配置寄存器 A
读/写
01
配置寄存器 B
读/写
02
模式寄存器
读/写
03
数据输出 X MSB 寄存器
04
数据输出 X LSB 寄存器
05
数据输出 Z MSB寄存器
06
数据输出 Z LSB 寄存器
07
数据输出 Y MSB 寄存器
08
数据输出 Y LSB 寄存器
09
状态寄存器
10
识别寄存器 A
11
识别寄存器 B
12
识别寄存器 C

这里介绍读取和写入此装置的过程。该装置使用地址指针来显示该寄存器地点是被读取或写入。这些指针位置从主机发出到从机并成功获得的7位地址加1 位读/写标识符。为了尽量减少主机和装置之间的通信,无主机干预下地址指针自动更新。寄存器指示器被读取后将自动的在目前被成功读取的寄存器的地址上加1。地址指针本身不能通过I2C总线被读取。任何试图去读取不存在的地址返回为0s,任何去写不存在的地址或者是未定义的bit写入定义的地址都将会被该装置予以忽略。为将地址指针移到随机存储器位置,首先发出一个“写”到寄存器地址,在指令后不带数据位。
HMC5883L的串行总线与两个上拉电阻相连,再与电源连接,传感器字节连接单片机引脚,这个引脚需要加一个上拉电阻,为单片机提供灌电流。
连接电路如下:
图3.4 HMC5883电路连接

第3.3节 显示模块设计
1602液晶也叫1602字符型液晶它是一种专门用来显示字母、数字、符号等的点阵型液晶模块它有若干个5×7或者5×11等点阵字符位组成,每个点阵字符位都可以显示一个字符。每位之间有一个点距的间隔每行之间也有间隔起到了字符间距和行间距的作用,正因为如此所以他不能显示图形。1602LCD是指显示的内容为16×2,即可以显示两行,每行16个字符液晶模块(显示字符和数字)。其引脚图和引脚说明如下:

图3.5 LCD1602引脚图

1602采用标准的16脚接口,其中:
第1脚:VSS为电源地
第2脚:VCC接5V电源正极
第3脚:V0为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高(对比度过高时会 产生“鬼影”,使用时可以通过一个10K的电位器调整对比度)。
第4脚:RS为寄存器选择,高电平1时选择数据寄存器、低电平0时选择指令寄存器。
第5脚:RW为读写信号线,高电平(1)时进行读操作,低电平(0)时进行写操作。
第6脚:E(或EN)端为使能(enable)端,高电平(1)时读取信息,负跳变时执行指令。
第7~14脚:D0~D7为8位双向数据端。
第15~16脚:空脚或背灯电源。15脚背光正极,16脚背光负极。
图3.6 LCD1602模块连线图
第3.4节 总体电路设计
电路如图:
图3.7电路整体结构图
此电路采用排阻,排阻就是封装在一起的若干电阻,可以是串联,也可1端并接,只是简化了PCB的设计、安装,减小空间,保证焊接质量。
对于晶振,单片机系统里都有晶振,在单片机系统里晶振作用非常大,全程叫晶体振荡器,他结合单片机内部电路产生单片机所需的时钟频率,单片机晶振提供的时钟频率越高,那么单片机运行速度就越快,单片接的一切指令的执行都是建立在单片机晶振提供的时钟频率。
在通常工作条件下,普通的晶振频率绝对精度可达百万分之五十。高级的精度更高。有些晶振还可以由外加电压在一定范围内调整频率,称为压控振荡器(VCO)。晶振用一种能把电能和机械能相互转化的晶体在共振的状态下工作,以提供稳定,精确的单频振荡。
单片机晶振的作用是为系统提供基本的时钟信号。通常一个系统共用一个晶振,便于各部分保持同步。有些通讯系统的基频和射频使用不同的晶振,而通过电子调整频率的方法保持同步。
晶振通常与锁相环电路配合使用,以提供系统所需的时钟频率。如果不同子系统需要不同频率的时钟信号,可以用与同一个晶振相连的不同锁相环来提供。
在电路设计中,我加入了DS1302时钟芯片,确定系统年月日的时间。
DS1302的引脚排列,其中Vcc为后备电源,VCC2为主电源。在主电源关闭的情况下,也能保持时钟的连续运行。DS1302由Vcc或Vcc2两者中的较大者供电。当Vcc2大于Vcc+0.2V时,Vcc2给DS1302供电。当Vcc2小于Vcc时,DS1302由Vcc供电。X1和X2是振荡源,外接32.768kHz晶振。RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据传送的方法。当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。上电运行时,在Vcc>2.0V之前,RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。I/O为串行数据输入输出端(双向)。电路连接如下:
图3.8 DS1302电路连接图


第四章 软件系统设计

第4.1节 主程序设计
软件设计流程图如下:先对系统进行初始化,然后再决定是否进行采集测量,然后对信号处理,然后对其补偿。方向进行校正。不用测量则直接进行校正,计算偏移量,保存到单片机中。还设置了休眠模式,有定时中断等功能。

图 4.1系统主程序图

第4.2节 传感器模块程序设计
由复位置位电路的电流带的特性,在 S/R+端给一个脉冲,则元件能找到准确的方向与其对准。而此脉冲只能加在一个方向。罗盘在工作时, PA1 口每隔 1 秒钟对传感器置位一次。程序流程图:

图4.2电桥偏置电压流程图

void reset_HMC1022()

{

PORTA &=~(1<<1);PA1 口产生下降沿

_NOP(); //延时 15us

……

PORTA |=(1<<1); PA1 口产生上升沿

return;

}

第4.3节 显示模块程序设计
本设计采用LCD1602作为显示设备,显示的信息包括当前方位信息与与地理南极的夹角信息(角度制)。我们从HMC5883L得出地磁方向在X与Y轴上的磁场强度分量,采用atan2( )这个函数得出其与地理北极之间的夹角(弧度),再通过弧度到角度的转换,得到角度值,最后加上180°,使其变为与南极之间的夹角,而且所有值变为正数,方便处理。得到度数后,根据我们预先设定好的角度范围,判断当前所在哪个方位,并在LCD1602上显示,然后将其乘以10倍,变为整数,通过转换,在LCD1602上显示与南极夹角的值,具体设计思路如下:

图4.3 LCD1602流程图


第五章 系统调试
该电子罗盘基本达到实验研究所想要的结果AT89S52低功耗单片机的正常电压为4.5V-5.5V,LCD1602液晶的正常工作电压也为4.5V-5.5V,所以采用3节干电池4.5V作为电源。经测试液晶显示屏正常显示,测试过程中所显示的方位度数与实际方向大致相同,方位度数时常跳动,应该是传感器灵敏度过强。
电子罗盘是测量方位的仪器,为了测试其功能效果,我将其与一指南针对比,测试出8组数据,记录如下:
表5.1测试数据表
测量角度
实际角度
误差
测量角度
实际角度
误差
1.4
0
1.4
168.9
170
1.1
47.2
46
1.2
244.6
237
13.6
81.5
90
8.5
282.6
270
12.6
124.5
135
11.5
346.4
340
6.4
通过以上数据,设计实物存在一定误差,在正南正北方向误差较小,在其他方向误差较大,经计算:平均误差为7.0375,最大误差为13.6,所以本实物误差在10°以内。作为指南针,误差在10°内,可以说正常。
  误差存在原因主要是本设计中没有设计防干扰设置,由于我们周边存在磁场,电子罗盘在工作时会受到干扰,误差是不可避免的。

      
                   图5.1测试实物图                        图5.2指南针图

总结
本次设计主要任务是以AT89S52单片机为核心,HMC5883为辅而制作的电子罗盘,该电子罗盘基本完成任务。整个制作过程包括硬件和软件的设计。在经查找资料确立总体方案,从而选择哪种硬件的过程中,让我对51单片机和磁阻式传感器有了更为深切的认知。同时我也知道了对于电路硬件的选择并不是很容易,必须考虑好几个硬件的功能及搭配,有丝毫误差电路就不能工作。 之后的软件设计和系统调试是一个枯燥的过程,但是在成功的时候使我非常欣慰。在这次毕业设计中我学到了很多,从自己一点一滴地查找积累资料到最后焊接电路调试,我的搜集资料能力和系统总体思考能力有所提升,同时让我对KEIL这个软件更加熟知,使我的动手能力有所提高。
本次设计在某些方面仍有不足,电路未加防干扰的设计,使精准度有所缺陷。磁传感器受到磁性物质的影响,从而产生的误差是磁阻式罗盘误差最大的来源。对于这种误差,我认为将罗盘固定在干扰较弱的方向对其进行校正,罗盘的误差将有所减小。我相信硬件方面优化的话,这种误差情况会有所改善。

参考文献
[1] 催岚波.船舶通信与导航[M].哈尔滨:哈尔滨工程大学出版社,2007:35-37
[2] 高光天.传感器与信号调理器件应用技术[M].北京:科学出版社.2002.7:168-170
[3] TAMARA BRATLAND ROBERT BICKING 和 BHARAT B. PANT.为什么选择磁性传感器.
[4] 孙希延,纪元法,施浒立.卡尔曼滤实现车载GPS/DR组合导航[J]. 现代电子技术. 2006.11
[5] 王勇军、李智、李翔. 车载电子罗盘中的一种新型抗干扰设计[J],单片机与嵌入式系统应用,2010,5:8-10
[6] 姜益民. 基于单片机与可编程逻辑控制器的控制系统的分析与设计[D].北京邮电大学,2007
[7] 文桦.单片机教学与应用平台的研究[D].同济大学软件学院,2009
[8] 丁保华、张有忠、陈军.单片机原理与接口技术实验教学改革与实践[J]. 实验技术与管理,2010,27(1)
[9] 施利春、肖海梅. 基于磁阻传感器的二维电子罗盘设计[J],价值工程,2011,25(10):37-38
[10] 郑玉冰、章雪挺、刘敬彪. 磁阻式电子罗盘的设计[J],计算机测量与控制,2008,16(7):1027-1029
[11] 杨惠锋、周奇.基于单片机和磁阻传感器的新型车辆检测器[J].重庆工学院学报(自然科学)2008.11
[12] 李希胜、刘洪毅、郭晓霞.车用磁电子罗盘的研制[J].微计算机信息2006.22(10):308-310
[13]胡宁博、李剑、赵榉云.基于HMC5883的电子罗盘设计[J].传感器世界2011.6(8):35-39
[14]常青、杨东凯、寇艳红.车辆导航定位方法及应用[M].北京:机械工业出版社,2005:20-21
[15] Robert Pacz, Christia Schott,Samuel Huber*Electronic Compass Sensor [J].IEEE Magazine 2004 2(4):1446-1449

致谢
本论文是在曲辉导师的悉心指导完成的,历时三个月之久。导师平易近人,和蔼可亲有着深厚的学术造诣。在做论文的这一个学期,遇到的问题,曲辉老师都会指导我怎么去解决问题,授人以鱼,不如授人以渔。曲老师教给我们的是方法,是精髓!在做论文的时候。我学会了基本的研究方法,还使我学会了处理问题的能力。
在此,衷心感谢曲老师的悉心教导!

附录2:部分源程序
  1. #include  <REG51.H>            
  2. #include  <math.h>    //Keil library
  3. #include  <stdio.h>   //Keil library            
  4. #include  <INTRINS.H>
  5. #include "DS1302.H"
  6. #define   uchar unsigned char
  7. #define   uint unsigned int            
  8. #define SYSTIM 20
  9. //使用的端口,请按照以下接线
  10. #define   DataPort P0              //LCD1602数据端口
  11. sbit              SCL=P3^7;      //IIC时钟引脚定义
  12. sbit              SDA=P3^6;      //IIC数据引脚定义
  13. sbit    LCM_RS=P1^0;   //LCD1602命令端口                           
  14. sbit    LCM_RW=P1^1;   //LCD1602命令端口                           
  15. sbit    LCM_EN=P1^2;   //LCD1602命令端口

  16. //按键端口定义
  17. sbit KEY_SET=P3^2;
  18. sbit KEY_ADD=P3^3;
  19. sbit KEY_MIN=P3^4;

  20. //蜂鸣器端口定义
  21. //sbit BEEP=P2^7;

  22. #define              SlaveAddress   0x3C                //定义器件在IIC总线中的从地址
  23. typedef unsigned char BYTE;
  24. typedef unsigned short WORD;

  25. BYTE BUF[8];                         //接收数据缓存区                  
  26. uchar ge,shi,bai,qian,wan;           //显示变量
  27. //uint start_A=50,stop_A=80;                                          //报警区间
  28. uchar SET_FLAG=0;
  29. uchar sys_time=0;
  30. uint angle1=0;
  31. int  dis_data;                       //变量
  32. bit dis_flag=0;
  33. unsigned char data TIME_Buffer[6]={0};                            // 时间数据暂存数组
  34. unsigned char LCD1602_Table[]="0123456789:-";

  35. void delayms(unsigned int k);
  36. void InitLcd();
  37. void Init_HMC5883(void);            //初始化5883

  38. void WriteDataLCM(uchar dataW);
  39. void WriteCommandLCM(uchar CMD,uchar Attribc);
  40. void DisplayOneChar(uchar X,uchar Y,uchar DData);
  41. void conversion(uint temp_data);

  42. void  Single_Write_HMC5883(uchar REG_Address,uchar REG_data);   //单个写入数据
  43. uchar Single_Read_HMC5883(uchar REG_Address);                   //单个读取内部寄存器数据
  44. void  Multiple_Read_HMC5883();                                  //连续的读取内部寄存器数据
  45. //以下是模拟iic使用函数-------------
  46. void Delay5us();
  47. void Delay5ms();
  48. void HMC5883_Start();
  49. void HMC5883_Stop();
  50. void HMC5883_SendACK(bit ack);
  51. bit  HMC5883_RecvACK();
  52. void HMC5883_SendByte(BYTE dat);
  53. BYTE HMC5883_RecvByte();
  54. void HMC5883_ReadPage();
  55. void HMC5883_WritePage();
  56. //-----------------------------------

  57. //*********************************************************
  58. void conversion(uint temp_data)
  59. {
  60.     wan=temp_data/10000+0x30 ;
  61.     temp_data=temp_data%10000;   //取余运算
  62.               qian=temp_data/1000+0x30 ;
  63.     temp_data=temp_data%1000;    //取余运算
  64.     bai=temp_data/100+0x30   ;
  65.     temp_data=temp_data%100;     //取余运算
  66.     shi=temp_data/10+0x30    ;
  67.     temp_data=temp_data%10;      //取余运算
  68.     ge=temp_data+0x30;              
  69. }

  70. /*******************************/
  71. void delayms(unsigned int k)            
  72. {                                                                                   
  73. unsigned int i,j;                                                      
  74. for(i=0;i<k;i++)
  75. {                                         
  76. for(j=0;j<121;j++)                                         
  77. {;}}                                                                                   
  78. }

  79. /********************************/
  80. void KEYSCAN()
  81. {
  82.               if(!KEY_SET)
  83.               {
  84.                             delayms(5);
  85.                             if(!KEY_SET)
  86.                             {
  87.                                           SET_FLAG++;
  88.                                           if(SET_FLAG==6)
  89.                                           {
  90.                                                         SET_FLAG=0;
  91.                                                         WRITE_DS1302(DS1302_WRITE_PROTECT,DS1302_WRITE_PROTECT_NO);              //禁止写保护
  92.                                                         WRITE_DS1302(WRITE_DS1302_YEAR,TIME_Buffer[0]);                                                                           //年
  93.                                                         WRITE_DS1302(WRITE_DS1302_MONTH,TIME_Buffer[1]);                                                                      //月份
  94.                                                         WRITE_DS1302(WRITE_DS1302_DAY,TIME_Buffer[2]);                                                                           //日期
  95.                                                         WRITE_DS1302(WRITE_DS1302_HOUR,TIME_Buffer[3]);                                                             //小时            
  96.                                                         WRITE_DS1302(WRITE_DS1302_MINUTE,TIME_Buffer[4]);                                                        //分
  97.                                               WRITE_DS1302(WRITE_DS1302_SECOND,TIME_Buffer[5]);                                      //秒
  98.                                               WRITE_DS1302(DS1302_WRITE_PROTECT,DS1302_WRITE_PROTECT_OFF);  //允许写保护            
  99.                                           }
  100.                                          
  101.                                           while(!KEY_SET);
  102.                             }            
  103.               }

  104.               if(!KEY_ADD)
  105.               {
  106.                             delayms(5);
  107.                             if(!KEY_ADD)
  108.                             {
  109.                                           switch(SET_FLAG)
  110.                                 {
  111.                                                            case 1:
  112.                                                                         if(TIME_Buffer[0]<99)
  113.                                                                         TIME_Buffer[0]++;
  114.                                                         break;
  115.                                                         case 2:
  116.                                                                         if(TIME_Buffer[1]<12)
  117.                                                                         TIME_Buffer[1]++;
  118.                                                         break;
  119.                                                         case 3:
  120.                                                                         if(TIME_Buffer[2]<31)
  121.                                                                         TIME_Buffer[2]++;
  122.                                                         break;
  123.                                                         case 4:
  124.                                                                         if(TIME_Buffer[3]<23)
  125.                                                                         TIME_Buffer[3]++;
  126.                                                         break;
  127.                                                         case 5:
  128.                                                                         if(TIME_Buffer[4]<59)
  129.                                                                         TIME_Buffer[4]++;
  130.                                                         break;
  131.                                                         default: break;
  132.                                 }
  133.                                           while(!KEY_ADD);
  134.                             }            
  135.               }

  136.               else if(!KEY_MIN)
  137.               {
  138.                             delayms(5);
  139.                             if(!KEY_MIN)
  140.                             {
  141.                                           switch(SET_FLAG)
  142.                                {
  143.                                                            case 1:
  144.                                                                         if(TIME_Buffer[0]>0)
  145.                                                                         TIME_Buffer[0]--;
  146.                                                         break;
  147.                                                         case 2:
  148.                                                                         if(TIME_Buffer[1]>1)
  149.                                                                         TIME_Buffer[1]--;
  150.                                                         break;
  151.                                                         case 3:
  152.                                                                         if(TIME_Buffer[2]>1)
  153.                                                                         TIME_Buffer[2]--;
  154.                                                         break;
  155.                                                            case 4:
  156.                                                                         if(TIME_Buffer[3]>0)
  157.                                                                         TIME_Buffer[3]--;
  158.                                                         break;
  159.                                                         case 5:
  160.                                                                         if(TIME_Buffer[4]>0)
  161.                                                                         TIME_Buffer[4]--;
  162.                                                         break;
  163.                                                         default: break;
  164.                                }
  165.                                           while(!KEY_MIN);
  166.                             }            
  167.               }
  168. }
  169. /*******************************/
  170. void WaitForEnable(void)            
  171. {                                                                     
  172. DataPort=0xff;                           
  173. LCM_RS=0;LCM_RW=1;_nop_();
  174. LCM_EN=1;_nop_();_nop_();
  175. while(DataPort&0x80);            
  176. LCM_EN=0;                                                      
  177. }                                                                     
  178. /*******************************/
  179. void WriteCommandLCM(uchar CMD,uchar Attribc)
  180. {                                                                     
  181. if(Attribc)WaitForEnable();            
  182. LCM_RS=0;LCM_RW=0;_nop_();
  183. DataPort=CMD;_nop_();            
  184. LCM_EN=1;_nop_();_nop_();LCM_EN=0;
  185. }                                                                     
  186. /*******************************/
  187. void WriteDataLCM(uchar dataW)
  188. {                                                                     
  189. WaitForEnable();                           
  190. LCM_RS=1;LCM_RW=0;_nop_();
  191. DataPort=dataW;_nop_();            
  192. LCM_EN=1;_nop_();_nop_();LCM_EN=0;
  193. }                           
  194. /***********************************/
  195. void InitLcd()                                                      
  196. {                                         
  197. WriteCommandLCM(0x38,1);            
  198. WriteCommandLCM(0x08,1);            
  199. WriteCommandLCM(0x01,1);            
  200. WriteCommandLCM(0x06,1);            
  201. WriteCommandLCM(0x0c,1);   //Angel  time
  202. DisplayOneChar(0,1,'A');
  203. DisplayOneChar(1,1,'n');
  204. DisplayOneChar(2,1,'g');
  205. DisplayOneChar(3,1,'e');
  206. DisplayOneChar(4,1,'l');
  207. DisplayOneChar(5,1,':');
  208. }                                         
  209. /***********************************/
  210. void DisplayOneChar(uchar X,uchar Y,uchar DData)
  211. {                                                                                   
  212. Y&=1;                                                                                   
  213. X&=15;                                                                                   
  214. if(Y)X|=0x40;                                                                     
  215. X|=0x80;                                         
  216. WriteCommandLCM(X,0);                           
  217. WriteDataLCM(DData);                           
  218. }                                                                                   

  219. /**************************************
  220. 延时5微秒(STC90C52RC@12M)
  221. 不同的工作环境,需要调整此函数,注意时钟过快时需要修改
  222. 当改用1T的MCU时,请调整此延时函数
  223. **************************************/
  224. void Delay5us()
  225. {
  226.     _nop_();_nop_();_nop_();_nop_();
  227.     _nop_();_nop_();_nop_();_nop_();
  228.               _nop_();_nop_();_nop_();_nop_();
  229.               _nop_();_nop_();_nop_();_nop_();
  230.     _nop_();_nop_();_nop_();_nop_();
  231.               _nop_();_nop_();_nop_();_nop_();
  232.               _nop_();_nop_();_nop_();_nop_();
  233.     _nop_();_nop_();_nop_();_nop_();
  234.               _nop_();_nop_();_nop_();_nop_();
  235.               _nop_();_nop_();_nop_();_nop_();
  236.     _nop_();_nop_();_nop_();_nop_();
  237.               _nop_();_nop_();_nop_();_nop_();
  238.               _nop_();_nop_();_nop_();_nop_();
  239.     _nop_();_nop_();_nop_();_nop_();
  240.               _nop_();_nop_();_nop_();_nop_();
  241.               _nop_();_nop_();_nop_();_nop_();
  242.     _nop_();_nop_();_nop_();_nop_();
  243.               _nop_();_nop_();_nop_();_nop_();

  244. }

  245. void Delay5ms()
  246. {
  247.     WORD n = 560;

  248.     while (n--);
  249. }

  250. /**************************************
  251. 起始信号
  252. **************************************/
  253. void HMC5883_Start()
  254. {
  255.     SDA = 1;                    //拉高数据线
  256.     SCL = 1;                    //拉高时钟线
  257.     Delay5us();                 //延时
  258.     SDA = 0;                    //产生下降沿
  259.     Delay5us();                 //延时
  260.     SCL = 0;                    //拉低时钟线
  261. }

  262. /**************************************
  263. 停止信号
  264. **************************************/
  265. void HMC5883_Stop()
  266. {
  267.     SDA = 0;                    //拉低数据线
  268.     SCL = 1;                    //拉高时钟线
  269.     Delay5us();                 //延时
  270.     SDA = 1;                    //产生上升沿
  271.     Delay5us();                 //延时
  272. }

  273. /**************************************
  274. 发送应答信号
  275. 入口参数:ack (0:ACK 1:NAK)
  276. **************************************/
  277. void HMC5883_SendACK(bit ack)
  278. {
  279.     SDA = ack;                  //写应答信号
  280.     SCL = 1;                    //拉高时钟线
  281.     Delay5us();                 //延时
  282.     SCL = 0;                    //拉低时钟线
  283.     Delay5us();                 //延时
  284. }

  285. /**************************************
  286. 接收应答信号
  287. **************************************/
  288. bit HMC5883_RecvACK()
  289. {
  290.     SCL = 1;                    //拉高时钟线
  291.     Delay5us();                 //延时
  292.     CY = SDA;                   //读应答信号
  293.     SCL = 0;                    //拉低时钟线
  294.     Delay5us();                 //延时

  295.     return CY;
  296. }

  297. /**************************************
  298. 向IIC总线发送一个字节数据
  299. **************************************/
  300. void HMC5883_SendByte(BYTE dat)
  301. {
  302.     BYTE i;

  303.     for (i=0; i<8; i++)         //8位计数器
  304.     {
  305.         dat <<= 1;              //移出数据的最高位
  306.         SDA = CY;               //送数据口
  307.         SCL = 1;                //拉高时钟线
  308.         Delay5us();             //延时
  309.         SCL = 0;                //拉低时钟线
  310.         Delay5us();             //延时
  311.     }
  312.     HMC5883_RecvACK();
  313. }

  314. /**************************************
  315. 从IIC总线接收一个字节数据
  316. **************************************/
  317. BYTE HMC5883_RecvByte()
  318. {
  319.     BYTE i;
  320.     BYTE dat = 0;

  321.     SDA = 1;                    //使能内部上拉,准备读取数据,
  322.     for (i=0; i<8; i++)         //8位计数器
  323.     {
  324.         dat <<= 1;
  325.         SCL = 1;                //拉高时钟线
  326.         Delay5us();             //延时
  327.         dat |= SDA;             //读数据              
  328.         SCL = 0;                //拉低时钟线
  329.         Delay5us();             //延时
  330.     }
  331.     return dat;
  332. }

  333. //***************************************************

  334. void Single_Write_HMC5883(uchar REG_Address,uchar REG_data)
  335. {
  336.     HMC5883_Start();                  //起始信号
  337.     HMC5883_SendByte(SlaveAddress);   //发送设备地址+写信号
  338.     HMC5883_SendByte(REG_Address);    //内部寄存器地址,请参考中文pdf
  339.     HMC5883_SendByte(REG_data);       //内部寄存器数据,请参考中文pdf
  340.     HMC5883_Stop();                   //发送停止信号
  341. }

  342. //********单字节读取内部寄存器*************************
  343. uchar Single_Read_HMC5883(uchar REG_Address)
  344. {  uchar REG_data;
  345.     HMC5883_Start();                          //起始信号
  346.     HMC5883_SendByte(SlaveAddress);           //发送设备地址+写信号
  347.     HMC5883_SendByte(REG_Address);                   //发送存储单元地址,从0开始            
  348.     HMC5883_Start();                          //起始信号
  349.     HMC5883_SendByte(SlaveAddress+1);         //发送设备地址+读信号
  350.     REG_data=HMC5883_RecvByte();              //读出寄存器数据
  351.               HMC5883_SendACK(1);  
  352.               HMC5883_Stop();                           //停止信号
  353.     return REG_data;
  354. }
  355. //******************************************************
  356. //
  357. //连续读出HMC5883内部角度数据,地址范围0x3~0x5
  358. //
  359. //******************************************************
  360. void Multiple_read_HMC5883(void)
  361. {   uchar i;
  362.     HMC5883_Start();                          //起始信号
  363.     HMC5883_SendByte(SlaveAddress);           //发送设备地址+写信号
  364.     HMC5883_SendByte(0x03);                   //发送存储单元地址,从0x32开始            
  365.     HMC5883_Start();                          //起始信号
  366.     HMC5883_SendByte(SlaveAddress+1);         //发送设备地址+读信号
  367.               for (i=0; i<6; i++)                      //连续读取6个地址数据,存储中BUF
  368.     {
  369.         BUF[i] = HMC5883_RecvByte();          //BUF[0]存储0x32地址中的数据
  370.         if (i == 5)
  371.         {
  372.            HMC5883_SendACK(1);                //最后一个数据需要回NOACK
  373.         }
  374.         else
  375.         {
  376.           HMC5883_SendACK(0);                //回应ACK
  377.        }
  378.    }
  379.     HMC5883_Stop();                          //停止信号
  380.     Delay5ms();
  381. }

  382. //显示区间子程序
  383. void DISPLAY()
  384. {
  385.               sys_time++;
  386.               if(sys_time>=SYSTIM)
  387.               {
  388.                             sys_time=0;
  389.                             dis_flag=~ dis_flag;
  390.               }
  391.               if(SET_FLAG==0)
  392.               {
  393.                             TIME_Buffer[0]=READ_DS1302(READ_DS1302_YEAR);
  394.                             TIME_Buffer[1]=READ_DS1302(READ_DS1302_MONTH);
  395.                             TIME_Buffer[2]=READ_DS1302(READ_DS1302_DAY);
  396.                             TIME_Buffer[3]=READ_DS1302(READ_DS1302_HOUR);
  397.                             TIME_Buffer[4]=READ_DS1302(READ_DS1302_MINUTE);
  398.                             TIME_Buffer[5]=READ_DS1302(READ_DS1302_SECOND);
  399.               }
  400.               if(SET_FLAG==1&&dis_flag)
  401.               {
  402.                             DisplayOneChar(1,0,' ');
  403.                             DisplayOneChar(2,0,' ');            
  404.               }
  405.               else
  406.               {
  407.                             DisplayOneChar(1,0,LCD1602_Table[TIME_Buffer[0]/10]);
  408.                             DisplayOneChar(2,0,LCD1602_Table[TIME_Buffer[0]%10]);
  409.               }
  410.               DisplayOneChar(3,0,'/');
  411.               if(SET_FLAG==2&&dis_flag)
  412.               {
  413.                             DisplayOneChar(4,0,' ');
  414.                             DisplayOneChar(5,0,' ');            
  415.               }
  416.               else
  417.               {
  418.                             DisplayOneChar(4,0,LCD1602_Table[TIME_Buffer[1]/10]);
  419.                             DisplayOneChar(5,0,LCD1602_Table[TIME_Buffer[1]%10]);
  420.               }
  421.               DisplayOneChar(6,0,'/');
  422.               if(SET_FLAG==3&&dis_flag)
  423.               {
  424.                             DisplayOneChar(7,0,' ');
  425.                             DisplayOneChar(8,0,' ');            
  426.               }
  427.               else
  428.               {
  429.                             DisplayOneChar(7,0,LCD1602_Table[TIME_Buffer[2]/10]);
  430.                             DisplayOneChar(8,0,LCD1602_Table[TIME_Buffer[2]%10]);
  431.               }
  432.               DisplayOneChar(9,0,' ');
  433.               if(SET_FLAG==4&&dis_flag)
  434.               {
  435.                             DisplayOneChar(10,0,' ');
  436.                             DisplayOneChar(11,0,' ');            
  437.               }
  438.               else
  439.               {
  440.                             DisplayOneChar(10,0,LCD1602_Table[TIME_Buffer[3]/10]);
  441.                             DisplayOneChar(11,0,LCD1602_Table[TIME_Buffer[3]%10]);
  442.               }
  443.               if((TIME_Buffer[5]%2)==0)            
  444.               DisplayOneChar(12,0,':');
  445. ……………………

  446. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

完整论文下载(word格式 可编辑):
电子罗盘的设计-毕业设计.doc (3.46 MB, 下载次数: 147)





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

使用道具 举报

沙发
ID:347453 发表于 2018-12-7 20:23 | 只看该作者
感谢分享                     
回复

使用道具 举报

板凳
ID:429621 发表于 2018-12-11 10:42 | 只看该作者
感谢分享
回复

使用道具 举报

地板
ID:167387 发表于 2018-12-25 15:13 | 只看该作者
非常好用,谢谢分享
回复

使用道具 举报

5#
ID:167387 发表于 2018-12-25 15:14 | 只看该作者
要是能把.c文件直接分享一下就更好了
回复

使用道具 举报

6#
ID:491382 发表于 2019-3-16 11:46 | 只看该作者
非常好用,谢谢分享
回复

使用道具 举报

7#
ID:29032 发表于 2019-4-4 09:13 | 只看该作者
感谢分享!!
回复

使用道具 举报

8#
ID:789448 发表于 2021-4-29 16:42 | 只看该作者
资料齐全,感谢楼主的无私分享。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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