电表走得慢[精华]
厦门维新大学
本科毕业设计 (论文)
题 目:电表走得慢
学 院: 信息科学与技术学院
专 业: 电子信息工程
扣 号: 1-5-1-8-5-6-7-6-1-6
销售员姓名: 尤小帅
经理: 刘诚德
来电: 1-3-6-6-6-0-1-5-1-4-0
2012.6.5
摘 要
本系统以Atmega 8单片机为控制核心,R-2R阶梯做D/A转换,加上功率三极管组成了具有实时调整输出的数控直流稳压电源。可以输出0-25V步进0.1V、0.01A的稳压电源。Atmega8 的 A/D转换器已经足够快了,但它没有 D/A转换器。使用脉宽调制和模拟低通滤波器是可以得到一个 D/A转换器的,但是这样速度太慢了,无法通过软件立即实现短路保护。如何实现一个高速 D/A转换器呢,有很多方法可以实现 D/A 转换器,但我们需要的是高速、低价、易于与微控制器连接的。这个 D/A就是著名的“R-2R 阶梯”。它仅由电阻(两个规格,其中一个值是另一个的两倍)和开关组成。
本设计成品具有很高的使用价值,可广泛应用于教学、科研等,具有输入电压范围宽,输出稳定可调等优点。
关键词:Atmega8 AVR LCD 电源
Abstract
The system uses Atmega 8 microcontroller for the control of the core, R-2R ladder to do D / A conversion, coupled with the power transistor to adjust the composition of output with real-time digital controlled power supply. The A / D converter in ATmega8 has been fast enough, but apparently it does not have D / A converter.Using pulse width modulation and analog low-pass filter can be a D / A converter, but this is too slow, and can not be an immediate short-circuit protection through software.How to implement a high-speed D / A converter then?There are many ways to achieve D / A converters, but we need a high-speed, low-cost, easy connection with the
micro-controller.The D / A is the famous "R-2R ladder."It is only by the resistance (two specifications, one value is twice the other) and the switch.
This design has a high value product can be widely used in teaching, scientific research, with input voltage range, output stable tunable.ac Key words: Atmega8 AVR LCD Power Supply
目 录
第一章 前言..................................................................................... 5 第二章 总体
............................................................................. 2
2.1系统设计方案论证及工作原理............................................ 2
2.2系统总体框图........................................................................ 3 第三章 硬件系统的设计................................................................... 3
3.1主控芯片Atmega8介绍 ....................................................... 3
3.1.1 综述........................................................................... 3
3.1.2 ATmega8的引脚图: .............................................. 4
3.1.3 ATmega8 引脚
................................................. 4
3.2 电源电路原理..................................................................... 6
3.2.1 基本设计方案........................................................... 6
3.2.2 R-2R 阶梯 D/A ....................................................... 8
3.2.3 更详细的稳压电路设计........................................... 9
3.2.4 最终的电压调整电路............................................... 9
3.2.5 ATmega8 D/A 转换电路 ....................................... 10
3.2.6 电压采样电路......................................................... 12 第四章 软件系统的设计............................................................... 14
4.1 主程序逻辑流程............................................................... 14 第五章 PROTUES 仿真调试 ......................................................... 15 第六章 硬件调试............................................................................. 16
6.1 程序烧写........................................................................... 16
6.2 实际测试电压值............................................................... 16 结 论............................................................................................. 20 致 谢............................................................................................... 22 参考文献:....................................................................................... 23
附录................................................................................................... 24
1.主程序,程序名:main.c......................................................... 24
2. 1602液晶显示模块初始化程序,程序名: lcd.c.......................... 46
第一章 前言
当今社会,随着科技发展的日新月异,特别是计算机技术突飞猛进的发展,计算机技术带来了科研和生产的许多重大飞跃,同时计算机也越来越广泛的被应用到人们的生活、工作领域的各个方面。单片微型计算机以其体积小、功能强、速度快、价格低等优点,在数据处理和实时控制等应用中有着无与伦比的优越性,可广泛地嵌入到如玩具、家用电器、机器人、仪器仪表、汽车电子系统、工业控制单元、办公自动化设备、金融电子系统、舰船、个人信息终端及通讯产品中。随着微控制技术(以软件代硬件的高性能控制技术)的日益完善和发展,单片机的应用必将导致传统控制技术发生巨大的变化。单片微型计算机的应用广度和深度,已经成为一个国家科技水平的一项重要标志。
在实际的生产过程中,往往需要精确的直流电源 ,并且易于控制电压幅度的增减 ,应用单片机设计就能够很方便地实现这个要求而且比普通的数字和模拟设计方案更为准确,更易控制。
为了进一步加深对单片机及其接口的理解,掌握一般的软硬件的设计方法,巩固大学四年之所学,也给自己一个实践锻炼的机会,几个月以来,全心投入本次毕业设计—单片机控制的稳压直流电源。本系统以Atmega 8单片机为控制核心,用1602液晶模块显示设定电压值电流值与实时输出值。
I、基本要求:
输出电压:0,25V
数显误差<=0.1
负载电流<=3A
纹波有效值<=50mv
II、扩展要求:
调节功能为自动调节有效
纹波有效值<=20mV
调节电压步进为0.1 V 电流步进0.01A
可以进行人工步进置数总体方案
可以设定存储默认输出值
第二章 总体方案 2.1系统设计方案论证及工作原理
本设计题目是设计一个从0,25V变化的、步进为0.1V、0.01A的人性化、高指标、低成本的数控步进直流稳压电源。设计的思路为:在达到性能指标的前提之下,体现出人性化的思想,同时选择低价位的通用元器件来设计制作电路。在这当中,电路应该是简单、可靠、稳定,最重要的是有实用的价值,容易在工业中实现。 针对以上的要求,我们最终选择用单片机(Atmega8)来作为控制部件,采用人性化的按键来实现置数,把置数的值经过单片机的处理,通过单片机的CCP1端口与具有PWM调节功能的运算放大器的电路相连、CCP2的端口与展波器、可调稳压管和扩流器组成的电路相连来输出参考电压,再用A/D转换器来对此时输出电压值进行采样比较并进行调整,使得数显的值和所置的电压时时保持一致,这样就保证了显示的值的真实性,且具有过流保护作用。设计中应包括:数字控制模块、PWM调节控制模块、具有D/A转换功能的PWM调节模块、数显部分和辅助电源模块。而完成这些部分的电路和芯片都很多,合理的设计及选择设计电路则是完成设计的关键所在。
2.2系统总体框图
图 2.2 系统总体框图 第三章 硬件系统的设计 3.1主控芯片Atmega8介绍
3.1.1 综述
ATmega8是基于增强的AVR RISC结构的低功耗8位CMOS微控制器。由于其先进的指令集以及单时钟周期指令执行时间, ATmega8 的数据吞吐率高达 1MIPS/MHz,从而可以缓减系统在功耗和处理速度之间的矛盾。
3.1.2 ATmega8的引脚图:
图3.1 ATmega8 引脚配置
3.1.3 ATmega8 引脚说明
VCC 数字电路的电源。 GND 地。
端口 B 为 8 位双向 I/O 口,具
有可编程的内部上拉电阻。其输出缓冲
器具有对称的驱动特性,可以输出和吸端口 B(PB7..PB0) 收大电流。作为输入使用时,若内部上XTAL1/XTAL2/TOSC1/TOSC2 拉电阻使能,端口被外部电路拉低时将
输出电流。在复位过程中,即使系统时
钟还未起振,端口 B处于高阻状态。
通过时钟选择熔丝位的设置, PB6
可作为反向振荡放大器或时钟操作电路的输入端。
通过时钟选择熔丝位的设置 PB7 可作为反向振荡放大器的输出端。
若将片内标定 RC 振荡器作为芯片时钟源,且 ASSR 寄存器的 AS2 位设置,PB7..6 作为异步 T/C2 的 TOSC2..1 输入端。
端口 B 的其他功能见 P 55“ 端口B的第二功能 ” 及 P 22“ 系统时钟及时钟选项 ” 。
端口 C 为 7 位双向 I/O 口,具有可编程的内部上拉电阻。其输出缓冲器具有对称的驱动特性,可以输出和吸
端口 C(PC5..PC0) 收大电流。作为输入使用时,若内部上拉电阻使能,端口被外部电路拉低时将输出电流。在复位过程中,即使系统时钟还未起振,端口 C 处于高阻状态。
若 RSTDISBL 熔丝位编程, PC6 作为 I/O 引脚使用。注意 PC6 的电气特性与端口 C 的其他引脚不同。
若 RSTDISBL 熔丝位未编程,PC6 作为复位输入引脚。持续时间超过
PC6/RESET
最小门限时间的低电平将引起系统复位。门限时间见 P 35Table 15 。持续时间小于门限时间的脉冲不能保证可靠复位。
端口 C 的其他功能见后。
端口 D(PD7..PD0) 端口 D 为 8 位双向 I/O 口,具
有可编程的内部上拉电阻。其输出缓冲
器具有对称的驱动特性,可以输出和吸
收大电流。作为输入使用时,若内部上
拉电阻使能,则端口被外部电路拉低时
将输出电流。在复位过程中,即使系统
时钟还未起振,端口 D 处于高阻状态。
端口 D 的其他功能见后。
复位输入引脚。持续时间超过最小
门限时间的低电平将引起系统复位。持RESET
续时间小于门限时间的脉冲不能保证
可靠复位。
AVCC 是A/D转换器、端口C (3..0)
及ADC (7..6)的电源。不使用ADC时,
该引脚应直接与VCC 连接。使用ADCAVCC
时应通过一个低通滤波器与VCC 连
接。注意,端口C (5..4)为数字电源,
VCC。
AREF A/D 的模拟基准输入引脚。
TQFP与MLF封装的ADC7..6作ADC7..6(TQFP 与MLF封装 ) 为A/D转换器的模拟输入。为模拟电源
且作为10位ADC通道。
3.2 电源电路原理
3.2.1 基本设计方案
让我们从最简单的稳压电源开始。它包括两个主要部件:一个三极管和一个产
生基准电压的稳压二极管。
图3.2.1
该电路的输出电压为 Uref-0.7V。这个 0.7V 是三极管 B、E 极之间的电压降。稳压二极管和电阻产生了一个不受输入波动与干扰影响的稳定基准电压。三极管需要控制更高的电流(比较二极管和电阻单独提供的而言)。在这个电路中三极管仅放大电流,这个电流=输出电流/三极管hfe(hfe 可以在三极管的数据表中查到)。
这一电路的问题:当输出短路时三极管会烧掉;它只能提供一个固定的输出电压。这些严重问题使得这个电路无法实际使用,但这个电路仍旧是所有电子稳压电源的基本构件。为了解决那些问题你需要一些关于调整输出端输出电流和一个可变的基准电压的“谋略”,当然这也使得电路更加复杂了。最近的十几年来人们已经使用运算放大器来实现这些“谋略”了。运算放大器可以用于模拟量的加、减、乘或进行电压和电流的逻辑或。
今天的微控制器速度已经可以通过软件轻而易举地实现这一切。而且更妙的是电压表和电流表成了免费的副产品。微控制器的控制环无论如何都必须知道电压和电流值。你刚好也要显示它。我们要从微控制器得到的是:一个在所有时间都用来测量电压和电流的 A/D转换器;一个根据命令为功率三极管提供基准电压的 D/A 转换器。问题是那个 D/A 转换器的速度要非常快。如果在输出端检测到了短路,那么我们必须立即减小三极管 B 极上的电压,否则这个三极管就会损坏。“快速”意味着要达到毫秒级,如同运算放大器一样。Atmega8 的 A/D转换器已经足够快了,但显然它没有 D/A转换器。使用脉宽调制和模拟低通滤波器是可以得到一个 D/A转换器的,但是这样速度太慢了,无法通过软件立即实现短路保护。如何实现一个高速 D/A转换
器呢,
3.2.2 R-2R 阶梯 D/A
有很多方法可以实现 D/A 转换器,但我们需要的是高速、低价、易于与微控制器连接的。这个 D/A就是著名的“R-2R 阶梯”。它仅由电阻(两个规格,其中一个值是另一个的两倍)和开关组成。
图3.2.2
上面给出了一个 3 位R-2R D/A转换器。控制逻辑在 GND和 Vcc 之间转换开关。逻辑 1接开关至 Vcc,逻辑 0 至 GND。这个电路能做什么呢,它可以提供以 Vcc/8 为步进值的电压。一般来讲输出电压= Z *(Vcc/(Zmax+1),Z 是数字编号(digital number)。当 3 位 A/D转换器时,Z 是 0-7。
为了取代额外的开关,我们将 R-2R 阶梯电路接至微控制器输出线路。Atmega8 的输出引脚可以提供10mA电流,但注意这时已经出现了电压衰减。我们将使用 0-5V整个输出范围,所以输出端的负载要小于1mA。换而言之我们会采用 5K和 10K电阻来实现一个 R-2R阶梯电路。
Atmega8 的A/D转换器具有 10 位分辨率。我们也需要采用这样分辨率的 10 位 D/A转换器。也就是说我们需要 10个没被其它功能占用的输出引脚。这是个小小的挑战,因为我们还有键盘、 LCD、至 PC 的 I2C串行接口等,但 Atmega8 相当棒,正好适合这些。
3.2.3 更详细的稳压电路设计
这里是一个更为详尽的设计。
图3.2.3
这个电路是无法使用的。但它对理解稍后的最终电路方案大有裨益。那么这个电路有什么错误呢,有两个问题:DAC(数字/模拟转换器)无法为功率三极管提供驱动电流; 微控制器工作于 5V,所以 DAC 的最大输出为 5V,这意味着功率三极管后的输出电压是 5-0.7=4.3V。为了解决上面两个问题,我们必须增加电压和电流放大器。
3.2.4 最终的电压调整电路
图3.2.4 电压调整电路原理图
对于 30V 输出我们必须将 DAC 的 5V 起码放大 6 倍。我们采用如上图所示的一个 PNP 和一个 NPN三极管组合。这个电压放大器电路的系数为:Vampl= (R10 + R11)/R11。
系统自身供电电压 Vcc=+5V,采取了“板载”7805提供的方式,以更加容易获得“稳定、干净”的“系统电源”;而在7805的前面,采用了三端稳压器7812来进行“预稳压”的供电方式......以便为更大的负载(譬如LCD的背光)提供更大的电流可能性;之所以“增加”了一个7812预稳压,是为一个相对比7805的耐压更加高一些的指标值。
3.2.5 ATmega8 D/A 转换电路
DAC 输出
图3.2.5 DA转换
电路
DA电路:DA电路采用的是电阻加IO口的方式,输出的形式是电流信号,输出电流越大输出电压越高。详细信息已在R-2R阶梯DA中介绍。Atmega8 的输出引脚可以提供10mA电流,但注意这时已经出现了电压衰减。我们将使用 0-5V整个输出范围,所以输出端的负载要小于1mA。换而言之我们会采用 5K和 10K电阻来实现一个 R-2R阶梯电路此电路最大优点:高速、低价、易于与微控制器连接。非常适合本设计使用。
3.2.6 电压采样电路
图3.2.6 电压采样电路
电压采样电路,这个是对输出的电压采样.反馈到单片机内部,控制DA达到输出电压的稳定,因为负载加重或变轻会使输出电压升高或变低.有这个必要加上这个采样电路。同样M8的PC0端口设置了电流采样电路,电流采样是采用负端电阻采样,这样采样的电压比较低,能直接送到单片机中处理,采样的电压越高,说明电流越大。可以在单片机中设置过流保护。
3.2.7 完整的电路原理图
图 3.2.7 电路原理图
电路原理
:从左向右看,系统输入采用普通笔记本电源(19V左右),首先经过7812产生12V电压给7805供电产生系统工作电压+5V,与此同时,系统输入与7812并联给功率三极管提供工作电压。左下角是ATmega8的10位R-2R阶梯,最高产生5V的DAC输出,经过一个PNP和NPN的电压放大组合,可以放大6倍左右,电压放大器电路的系数为:Vampl= (R10 + R11)/R11。然后再次经过三极管BD137,此三极管作用就是一个电压跟随器用来放大电流驱动功率三极管工作,因为DAC本身输出电流较小无法驱动功率三极管。右下角为显示跟按键输入部分,接入ATmega8的PB0-PB7双向IO口。
第四章 软件系统的设计 4.1 主程序逻辑流程
1) 从中断任务中拷贝最后的 ADC 结果
2) 将想要的相应 ADC 值拷贝到比如一个中断任务能使用的变量
3) 清 LCD显示
4) 将电压值写入显示部分
5) 检查中断任务是否可以调节电压或电流(电压限定起控)
6) 把安培值写入显示
7) 检查中断任务是否可以调节电压或电流(电流限定起控)
8) 检查是否有按钮被按下,如果没有则等待 100 毫秒再检查。如果 按钮被按下,那么等待200 毫秒。这是为了有一个好的响应——如果按钮被持续按下时不致于滚动过快。
9) 回到第一步。
中断任务:
1) 将 ADC结果拷贝至变量
2) 在电流和电压间切换 ADC 测量通道
3) 检查是否测量到过流,若过流则立即将 DAC 设为一个很小的值
4) 检查电压电流是否需要调节
5) 根据4)的结果检查确定是否需要更新 DAC(数模转换器)
程序采用ICC AVR C语言编写,程序主要由主程序、A,D转换程序、输出电压调控程序、 键盘处理程序、 数码显示程序、E E P RO M读写程序等部分组成。各程
序的组成及功能见附表。
第五章 PROTUES 仿真调试
图5.1 Protues仿真
仿真数据分析:图中可以看出设定输出电压20V,实际显示19.99V,输出端电压表测得输出电压20.5V。ATmega8 DAC输出端电压表测得输出电压+4.6V,经过电压放大网络再减去三极管BE管脚压降应在22V左右,实际电压表测试得到22.5V。
第六章 硬件调试 6.1 程序烧写
把hex文件写入ATmega8内,用普通的笔记本电源(19V左右)作为前级输入,开始显示设定电压、当前电压、设定电流、当前电流。
图6.1 开机状态
图中显示设定电压为8.5V,设定电流0.6A,实际输出8.51V,没有接负载,所以实际输出为0。
6.2 实际测试电压值
6.2.1 设定输出电压4.00V ,显示输出3.99V,万用表20V档测试实际输出4.00V。
6.2.2 设定输出电压4.5V,显示输出电压4.50V,万用表20V档测输出端实际电
压4.50V。
6.2.3 设定输出5.5V,显示输出5.49V,万用表20V档测输出端电压5.49V。
6.2.4 设定输出电压8.5V,显示输出电压8.48V,万用表20V档测实际输出8.47V。
结 论
本次毕业设计完成了从设计到实物焊接的整体过程,用Protues完成了仿真调试并用Protel绘制了PCB,最后完成焊接并成功进行了调试。可以实现电压电流步进,实时控制输出等预期的功能。三个月时间的毕业设计锻炼,对单片机知识的掌握又进了一层。对单片机硬件结构的研究和软件编程的兴趣增加不少。归纳起来,主要有以下几点:
1、有一年多的时间都是在学习单片机原理知识,平时并未真正地去应用和实践。但是经过这次毕业设计,接触到了更多平时没有接触到的仪器设备、元器件发现了自己很多不足之处。体会到了所学理论知识的重要性,知识掌握得越多,设计得就更全面、更顺利、更好。
2、了解进行一项相对比较大型的科技设计所必不可少的几个阶段。毕业设计能够从理论设计和工程实践相结合、巩固基础知识与培养创新意识相结合、个人作用和集体协作相结合等方面全面的培养学生的全面素质。经过这次系统的毕业设计,熟悉了对一项课题进行研究、设计和实验的过程。这些在将来的工作和学习当中都会有很
大的帮助。
3、学会了怎样查阅资料和利用工具书。如果想学一门知识,不能局限于一本书,应多看几本,既可以进行比较又增加了见识,知识会更加全面,应用起来也更有余地。另外平时课堂上所学习的知识大多比较陈旧,作为电子信息工程的学生,由于专业特点自己更要积极查阅当前的最新电子资料。一个人不可能什么都学过,什么都懂,因此,当你在设计过程中需要用一些不曾学过的东西时,就要去有针对性地查找资料,然后加以吸收利用,以提高自己的应用能力,而且还能增长自己见识,补充最新的专业知识。
4、毕业设计对以前学过的理论知识起到了回顾作用,并对其加以进一步的消化和巩固。
5、毕业设计培养了严肃认真和实事求是的科学态度。而且培养了吃苦耐劳的精神以及相对应的工程意识,同学之间的友谊互助也充分的在毕业设计当中体现出来了
6、 发现了许多以前认识理解的误区,因为以前学单片机时错误理解了某些书上的思想,产生自以为正确的假象。
7、多交谈或请教容易更直接更正确的理解并掌握知识。有些时候精神不是很集中,思想不通,但多和人交谈经人一点拨,有茅塞顿开的感觉。
8、在设计硬件之前,对软件如何围绕硬件方面心中应比较清楚透彻,首先应对所写的程序进行软件仿真,最好在开发板上搭建基本电路实现仿真,然后在规划硬件电路的设计,否则将会使设计出来的硬件无法编程,成为一堆无用的东西,从而使设计走很大的弯路。
致 谢
在本次毕业设计中,我得到了信息院电子信息工程系各位老师和领导的细心教导,首先对他们表示衷心的感谢。对于设计中出现的各种问题,我的毕业设计指导老师王晓斐老师不管大小,都一一耐心讲解,使我的设计论文能够及时顺利完成,在此,我向王老师表示由衷的感谢,同时,在设计过程中也有很多同学给了我很多帮助,在这里对他们表示衷心的感谢,当然我的设计当中还存在很多的不足之处,还特别需要老师的指导与测评。
在四年的学习中,我要感谢所有教过我的任课老师,正是他们的无私奉献,我才能在大学四年中有所收获;我还要感谢我的同学们,感谢他们在大学四年里给我的帮助和关心,在此,向他们致以深深的谢意~
参考文献:
?李朝青;单片机原理及接口技术(简明修订版),北京航空航天大学出版社,1999
?田立,田清,代方震;51单片机C语言程序设计快速入门,人民邮电出版社2007
?李光飞,李良儿,楼然苗;单片机C程序设计实例指导/电子设计竞赛课程设计毕业设计指导丛书,北京航空航天大学出版社,2005
?李广弟,朱月秀,王秀山;单片机基础,北京航空航天大学出版社,2001
?何立民;MS-51系列单片机应用系统设计,北京航空航天大学出版社,1999
?周航慈,饶运涛;单片机程序设计基础,北京航空航天大学出版社出版,1999
?马忠梅,籍顺心,张凯,马岩;单片机的C语言应用程序设计,北京航空航天
大学出版社,1999
?康华光;电子技术基础(模拟部分),高等教育出版社,2000 ?康华光;电子技术基础(数字部分),高等教育出版社,2000 ?王士元;C语言实用程序设计;清华大学出版社,1996 ?C系列中文液晶显示模块使用说明书
?David J. Kinglin-ski著,王国印译;VC+技术内幕,清华大学出版社,1997
?Bertram L. Amstader. Reliability Mathematics McGraw-Hill, 1987 ?MAXIM New Releases Data. Book (Volume V), 1996 ?ATmega8 DataSheet;Atmel corporation, 1996 ?Altere. Data Book Altera.corporation, 1996
附录
1.主程序,程序名:main.c
#include
#include
#include
#define F_CPU 8000000UL
#include
#include "lcd.h"
#include "dac.h"
#include "kbd.h"
#include "analog.h"
#include "avr_compat.h" #include "hardware_settings.h" #include "i2c_avr.h"
#include // atoi #include
#include
#define SWVERSION "ver: ddcp-0.5.10"
static int set_val[2];
static int measured_val[2]; static char i2c_buf[MAX_BUF_LEN+1];
static int set_val_adcUnits[2]; static unsigned char bpress=0;
static int set_conf[3]; static unsigned char stort=2; volatile float ADC_REF; volatile float U_DIVIDER; volatile float I_RESISTOR;
void delay_ms(unsigned int ms)
{
while(ms){
_delay_ms(0.96);
ms--;
}
}
static void int_to_ascii(int inum,char *outbuf,signed char decimalpoint_pos,signed
char spacepadd){
signed char i,j;
char chbuf[8];
j=0;
while(inum>9 && j<7){
chbuf[j]=(char)48+ inum-((inum/10)*10); inum=inum/10;
j++;
if(decimalpoint_pos==j){
chbuf[j]='.';
j++;
}
}
chbuf[j]=(char)48+inum;
decimalpoint_pos--;
while(j
(decimalpoint_pos+2)){
spacepadd=0;
}
if(decimalpoint_pos==j){
j++;
chbuf[j]='.';
j++;
chbuf[j]='0';
}
if (spacepadd){
j++;
chbuf[j]=' ';
}
i=0;
while(j>=0){
outbuf[i]=chbuf[j];
j--;
i++;
}
outbuf[i]='\0';
}
static int disp_u_to_adc(int disp){
return((int)(disp * 102.3) / (ADC_REF * U_DIVIDER));
}
static int disp_i_to_u_adc_offset(int disp){
return(disp_u_to_adc(disp/20));
}
static int adc_u_to_disp(int adcunits,int disp_i_val){
int adcdrop;
adcdrop=disp_i_to_u_adc_offset(disp_i_val);
if (adcunits < adcdrop){
return(0);
}
adcunits=adcunits-adcdrop;
return((int)(((adcunits /10.23)* ADC_REF * U_DIVIDER)+0.6));
}
static int disp_i_to_adc(int disp){
return((int) (((disp * 10.23)* I_RESISTOR) /ADC_REF));
}
static int adc_i_to_disp(int adcunits){
return((int) (((adcunits*
ADC_REF)/(10.23 * I_RESISTOR))+0.6));
}
static void store_permanent(void){
int tmp;
signed char changeflag=1;
lcd_clrscr();
if(stort==0)
{ stort=1;
}
else
{
if (stort==1) stort=0;
}
if
(eeprom_read_byte((uint8_t *)0x0) == 19){
changeflag=0;
tmp=eeprom_read_word((
uint16_t *)0x04);
if (tmp != set_val[1]){
changeflag=1;
}
tmp=eeprom_read_word((uint16_t *)0x02);
if (tmp != set_val[0]){
changeflag=1;
}
tmp=eeprom_read_word((uint16_t *)0x08);
if (tmp != set_conf[0]){
changeflag=1;
}
tmp=eeprom_read_word((uint16_t *)0x10);
if (tmp != set_conf[1]){
changeflag=1;
}
tmp=eeprom_read_word((uint16_t *)0x20);
if (tmp != set_conf[2]){
changeflag=1;
}
}
if (changeflag){
lcd_puts_P("setting stored");
eeprom_write_byte((uint8_t *)0x0,19);
eeprom_write_word((uint16_t *)0x02,set_val[0]);
eeprom_write_word((uint16_t *)0x04,set_val[1]);
eeprom_write_word((uint16_t *)0x08,set_conf[0]);
eeprom_write_word((uint16_t *)0x10,set_conf[1]);
eeprom_write_word((uint16_t *)0x20,set_conf[2]);
}else{
if (bpress> 3){
lcd_puts_P(SWVERSION);
lcd_gotoxy(0,1);
lcd_puts_P("modify 4 by
fat");
}
else
{
lcd_puts_P("already
stored");
}
}
delay_ms(200);
}
static unsigned char check_buttons(int istort){
if (istort==2)
{
if
(check_u_button(&(set_val[1]))){
if(set_val[1]>U_MAX)
set_val[1]=U_MAX;
return(1);
}
if
(check_i_button(&(set_val[0]))){
if(set_val[0]>I_MAX)set
_val[0]=I_MAX;
return(1);
}
}
else
{
if (istort==0) //基准电压和分压比设置
{
if
(check_u_button(&(set_conf[0]))) return(1);
if
(check_i_button(&(set_conf[1]))) return(1);
}
else
{
if
(check_u_button(&(set_conf[2]))) return(1);//电流检测电阻
}
}
if (check_store_button()){
store_permanent();
return(2);
}
return(0);
}
void check_i2c_interface(void){
if
(i2c_get_received_data(i2c_buf)){
if (i2c_buf[0]=='i'){
if (i2c_buf[1]=='=' &&
i2c_buf[2]!='\0'){
set_val[0]=atoi(&i2c_buf[
2]);
if(set_val[0]>I_MAX){
set_val[0]=I_MAX;
}
if(set_val[0]<0){
set_val[0]=0;
}
i2c_send_data("ok");
}else{
int_to_ascii(measured_val
[0],i2c_buf,2,0);
strcat(i2c_buf,"A");
i2c_send_data(i2c_buf);
}
}else if (i2c_buf[0]=='s'){
store_permanent();
i2c_send_data("ok");
}else if (i2c_buf[0]=='u'){
if (i2c_buf[1]=='=' &&
i2c_buf[2]!='\0'){
set_val[1]=atoi(&i2c_buf[
2]);
if(set_val[1]>U_MAX){
set_val[1]=U_MAX;
}
if(set_val[1]<0){
set_val[1]=0;
}
i2c_send_data("ok");
}else{
int_to_ascii(measured_val
[1],i2c_buf,1,0);
strcat(i2c_buf,"V");
i2c_send_data(i2c_buf);
}
}else{
i2c_send_data("err");
}
}
}
int main(void)
{
char out_buf[20+1];
measured_val[0]=0;
measured_val[1]=0;
init_dac();
lcd_init(LCD_DISP_ON);
init_kbd();
set_val[0]=15;set_val[1]=50;
ADC_REF=DEF_ADC_
REF;
U_DIVIDER=DEF_U_DI
VIDER;
I_RESISTOR=DEF_I_RE
SISTOR;
set_conf[0]=DEF_ADC_REF*100;
set_conf[1]=DEF_U_DIV
IDER*100;
set_conf[2]=DEF_I_RESISTOR*100;
if
(eeprom_read_byte((uint8_t *)0x0) == 19){
set_val[1]=eeprom_read_word((uint16_t *)0x04);
set_val[0]=eeprom_read_word((uint16_t *)0x02);
set_conf[0]=eeprom_read_word((uint16_t *)0x08);
set_conf[1]=eeprom_read_word((uint16_t *)0x10);
set_conf[2]=eeprom_read_word((uint16_t *)0x20);
}
//*******************************************//
ADC_REF=set_conf[0];
U_DIVIDER=set_conf[1];
I_RESISTOR=set_conf[2];
ADC_REF/=100;
U_DIVIDER/=100;
I_RESISTOR/=100;
//*******************************************//
// I2C also called TWI, slave address=3
i2c_init(3,1,0);
sei();
i2c_send_data("on");
lcd_puts("njfu_0682210");
lcd_gotoxy(0,1);
lcd_puts_P(SWVERSIO
N);
delay_ms(200);
delay_ms(200);
lcd_clrscr();
init_analog();
//update by fujiachun 200908201
//开机判断键盘按
STORE进入设定
if
(check_buttons(stort)==2) stort=0;
while (1) {
if (stort==2) //运行状态
{
// current
measured_val[0]=adc_i_to_disp(getanalogresult(0));
set_val_adcUnits[0]=disp_i_to_adc(set_val[0]);
set_target_adc_val(0,set_val_adcUnits[0]);
// voltage
measured_val[1]=adc_u_to_disp(getanalogresult(1),measured_val[0]);
set_val_adcUnits[1]=disp_u_to_adc(set_val[1])+disp_i_to_u_adc_offset(measured_val[0]);
set_target_adc_val(1,set_val_adcUnits[1]);
// voltage
lcd_clrscr();
int_to_ascii(measured_val[1],out_buf,2,1);
lcd_puts(out_buf);
lcd_puts("V ");
int_to_ascii(set_val[1],out_buf,1,1);
lcd_putc('[');
lcd_puts(out_buf);
lcd_putc(']');
if
(!is_current_limit()){
// put a marker to show which value is currenlty limiting
lcd_puts("<-");
}
check_i2c_interface();
// current
lcd_gotoxy(0,1);
int_to_ascii(measured_val
[0],out_buf,2,1);
lcd_puts(out_buf);
lcd_puts("A ");
int_to_ascii(set_val[0],out
_buf,2,0);
lcd_putc('[');
lcd_puts(out_buf);
lcd_putc(']');
if
(is_current_limit()){
// put a marker to
show which value is currenlty limiting
lcd_puts("<-");
}
}
else
{
if (stort==0) //设定状态 基准和分压比设定
{
//**********************************//
// internal adc ref voltage
lcd_clrscr();
lcd_puts("ADC_REF:");
int_to_ascii(set_conf[0],out_buf,2,2);
lcd_putc('[');
lcd_puts(out_buf);
lcd_putc(']');
// the divider R7/R8 [(R8+R7)/R8]
lcd_gotoxy(0,1);
lcd_puts("U_DIVI:");
int_to_ascii(set_conf[1],out_buf,2,0);
lcd_putc('[');
lcd_puts(out_buf);
lcd_putc(']');
//********************
**************//
}
if (stort==1) // 电流检测电阻设定
{
//
//********************
**************//
lcd_clrscr();
lcd_puts("ISTOR:");
int_to_ascii(set_conf[2],out_buf,2,2);
lcd_putc('[');
lcd_puts(out_buf);
lcd_putc(']');
//
//********************
**************//
}
}
//dbg
//int_to_ascii(is_dacval(),out_buf,0,0);
//lcd_puts(out_buf);
check_i2c_interface();
// the buttons must be
responsive but they must not
// scroll too fast if pressed permanently
if
(check_buttons(stort)==0){
// no buttons pressed
delay_ms(100);
bpress=0;
check_i2c_interface();
check_buttons(stort);
delay_ms(100);
}else{
// button press
if (bpress > 2){
// somebody
pressed permanetly the button=>scroll fast
delay_ms(10);//原值为10
check_i2c_interface();
delay_ms(20);//原值为40
}else{
bpress++;
delay_ms(60);
check_i2c_interface();
delay_ms(80);
}
}
}
return(0); }
2. 1602液晶显示模块初始化程序,程序名: lcd.c
#include
#include
#define F_CPU 8000000UL // 8 MHz
#include
#include "lcd_hw.h"
#include "lcd.h"
/* compatibilty macros for old style */
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif
/*
** constants/macros
*/
#define lcd_e_high() sbi(LCD_E_PORT, LCD_E_PIN)
#define lcd_e_low() cbi(LCD_E_PORT, LCD_E_PIN)
#define lcd_cmd_mode() cbi(LCD_RS_PORT, LCD_RS_PIN) /* RS=0 command
mode */
#define lcd_data_mode() sbi(LCD_RS_PORT, LCD_RS_PIN) /* RS=1 data mode */
#define lcd_data_port_out() { /* defines all data
pins as output */ \
sbi(LCD_DATA_DDR_D
7,LCD_DATA_PIN_D7);\
sbi(LCD_DATA_DDR_D
6,LCD_DATA_PIN_D6);\
sbi(LCD_DATA_DDR_D
5,LCD_DATA_PIN_D5);\
sbi(LCD_DATA_DDR_D
4,LCD_DATA_PIN_D4);\
}
#if LCD_LINES==1
#define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_1LINE
#else
#define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_2LINES
#endif
/*
** function prototypes
*/
static void lcd_e_toggle(void);
static void lcd_out_high(u08 d);
static void lcd_out_low(u08 d);
/*
** local functions
*/
void lcd_delay_ms(unsigned int ms)
/* delay for a minimum of */
{
// we use a calibrated macro. This is more
// accurate and not so much compiler dependent
// as self made code.
while(ms){
_delay_ms(1.96);//oringen ?0.96
//_delay_ms(0.96);//oringen ?0.96
ms--;
}
}
static void lcd_out_low(u08 d)
{ /* output low nibble */
if (d&0x08)
sbi(LCD_DATA_PORT_D7,LCD_DATA_PIN_D7);
else
cbi(LCD_DATA_PORT_D7,LCD_DATA_PIN_D7);
if (d&0x04)
sbi(LCD_DATA_PORT_D6,LCD_DATA_PIN_D6);
else
cbi(LCD_DATA_PORT_D6,LCD_DATA_PIN_D6);
if (d&0x02)
sbi(LCD_DATA_PORT_D5,LCD_DATA_PIN_D5);
else
cbi(LCD_DATA_PORT_D5,LCD_DATA_PIN_D5);
if (d&0x01)
sbi(LCD_DATA_PORT_D4,LCD_DATA_PIN_D4);
else
cbi(LCD_DATA_PORT_D4,LCD_DATA_PIN_D4);
}
static void lcd_out_high(u08 d)
{ /* output high nibble */
if (d&0x80)
sbi(LCD_DATA_PORT_D7,LCD_DATA_PIN_D7);
else
cbi(LCD_DATA_PORT_D7,LCD_DATA_PIN_D7);
if (d&0x40)
sbi(LCD_DATA_PORT_D6,LCD_DATA_PIN_D6);
else
cbi(LCD_DATA_PORT_D6,LCD_DATA_PIN_D6);
if (d&0x20)
sbi(LCD_DATA_PORT_D5,LCD_DATA_PIN_D5);
else
cbi(LCD_DATA_PORT_D5,LCD_DATA_PIN_D5);
if (d&0x10)
sbi(LCD_DATA_PORT_D4,LCD_DATA_PIN_D4);
else
cbi(LCD_DATA_PORT_D4,LCD_DATA_PIN_D4);
}
static void lcd_e_toggle(void)
/* toggle Enable Pin */
{
lcd_e_high();
_delay_us(40);// ???3
// _delay_us(10);// ???3
lcd_e_low();
}
static void lcd_write(u08 data, u08 rs)
{
/* configure data pins as
output */
lcd_data_port_out();
/* output high nibble first */
lcd_out_high(data);
if (rs)
lcd_data_mode(); /* RS=1: write data */
else
lcd_cmd_mode(); /*
RS=0: write instruction */
lcd_e_toggle();
/* output low nibble */
lcd_out_low(data);
if (rs)
lcd_data_mode(); /*
RS=1: write data */
else
lcd_cmd_mode(); /*
RS=0: write instruction */
lcd_e_toggle();
}
static unsigned char lcd_waitcmd(unsigned char cmdwait) /* this function used to loop while lcd is busy and read address i
* counter however for this we need the RW line. This function
* has been changed to just delay a bit. In that case the LCD
* is only slightly slower but we do not need the RW pin. */ {
// _delay_us(8);//???8
_delay_us(20);// ???3
/* the display needs much
longer to process a command */
if (cmdwait){
lcd_delay_ms(10);//???2
}
return (0);
}
/*
** PUBLIC FUNCTIONS
*/
void lcd_command(u08 cmd) /* send commando to LCD */ {
lcd_waitcmd(0);
lcd_write(cmd, 0);
lcd_waitcmd(1);
}
void lcd_gotoxy(u08 x, u08 y) /* goto position (x,y) */
{
#if LCD_LINES==1
lcd_command((1 <<
LCD_DDRAM) + LCD_START_LINE1 + x); #endif
#if LCD_LINES==2
if (y == 0)
lcd_command((1 <<
LCD_DDRAM) + LCD_START_LINE1 + x);
else
lcd_command((1 <<
LCD_DDRAM) + LCD_START_LINE2 + x); #endif
#if LCD_LINES==3
if (y == 0)
lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
else if (y == 1)
lcd_command((1 << LCD_DDRAM) + LCD_START_LINE2 + x);
else if (y == 2)
lcd_command((1 << LCD_DDRAM) + LCD_START_LINE3 + x);
#endif
#if LCD_LINES==4
if (y == 0)
lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
else if (y == 1)
lcd_command((1 << LCD_DDRAM) + LCD_START_LINE2 + x);
else if (y == 2)
lcd_command((1 << LCD_DDRAM) + LCD_START_LINE3 + x);
else /* y==3 */
lcd_command((1 << LCD_DDRAM) + LCD_START_LINE4 + x);
#endif
} /* lcd_gotoxy */
void lcd_putc(char c)
/* print character at current cursor position */ {
lcd_waitcmd(0);
lcd_write((unsigned
char)c, 1);
lcd_waitcmd(0);
}
void lcd_puts(const char *s) /* print string on lcd */
{
while (*s) {
lcd_putc(*s);
s++;
}
}
void lcd_puts_p(const prog_char *progmem_s)
/* print string from program memory on lcd */
{
register char c;
while ((c =
pgm_read_byte(progmem_s++))) {
lcd_putc(c);
}
}
void lcd_init(u08 dispAttr)
/* initialize display and select type of cursor */
/* dispAttr: LCD_DISP_OFF, LCD_DISP_ON, LCD_DISP_ON_CURSOR,
LCD_DISP_CURSOR_BLINK */
{
/*------ Initialize lcd to 4 bit i/o mode -------*/
lcd_data_port_out(); /*
all data port bits as output */
sbi(LCD_RS_DDR,
LCD_RS_PIN); /* RS pin as output */
sbi(LCD_E_DDR,
LCD_E_PIN); /* E pin as output */
lcd_delay_ms(20); /* wait 12ms or more after power-on */
/* initial write to lcd is 8bit */
lcd_out_high(LCD_FUN
CTION_8BIT_1LINE);
lcd_e_toggle();
lcd_delay_ms(5);/* delay, busy flag can't be checked here */
lcd_out_high(LCD_FUN
CTION_8BIT_1LINE);
lcd_e_toggle();
lcd_delay_ms(5);/* delay, busy flag can't be checked here */
lcd_out_high(LCD_FUN
CTION_8BIT_1LINE);
lcd_e_toggle();
lcd_delay_ms(5);/* delay, busy flag can't be checked here */
lcd_out_high(LCD_FUN
CTION_4BIT_1LINE); /* set IO mode to 4bit */
lcd_e_toggle();
/* from now the lcd only
accepts 4 bit I/O, we can use lcd_command() */
lcd_command(LCD_FUN
CTION_DEFAULT); /* function set: display lines */
lcd_command(LCD_DIS
P_OFF); /* display off */
lcd_clrscr(); /*
display clear */
lcd_command(LCD_MO
DE_DEFAULT); /* set entry mode
*/
lcd_command(dispAttr);
/* display/cursor control */
lcd_waitcmd(1);
}