|
|

楼主 |
发表于 2012-8-10 01:30:14
|
显示全部楼层
来自: 中国上海
炎炎夏日需要一个清凉的地 - 自制水冷系统(十一 指尖的思绪之程序篇)
前段时间接手了一个项目,所以DIY的进程有些停滞。实际编写的程序并没有多长时间,得益于Keil这个强大的IDE。能在第一次做51开发的时候,如此顺利的完成代码。
不多说废话了,说明下代码的具体思路。具体思路根据(六 控制系统原理图)进行拆分。分为温度显示部分、指示灯、温度获取和继电器操控部分。
LEDDisplay.c --- 温度显示模块
PilotLamp.c --- 指示灯控制模块
Temperature.c
12b20Temperature.asm --- 温度读取模块
Relays.c --- 继电器控制部分
系统主要控制的指数
1、读取温度的间隔时间
2、在调整温度的时候显示制冷控制温度时间。
3、当达到制冷温度设定值时,关闭或开启控制器一个缓冲量。防止在阀值的时候出现上下跳变问题。
4、控制调整温度的调节范围和调节量
开发中的主要阻力还是来至于温控IC。18B20的时序操作非常严格。如果要自己写调试太耗时间。因此直接从前辈取材,汇编是控制时序是最精确的,在网上找到的很多代码基本也是以汇编为主。我的代码是从杜洋老师已调整好读写时序的汇编代码改编而来。做了些小改动,把温度的精度提高到0.06°C。
另外一个让人抓狂的问题,没能找到根源解决。汇编和C混合开发中,变量的存储地址出现混乱。主程序部分的变量被汇编中的地址操作覆写,导致变量值被冲掉。不知道动了哪根筋,原来木问题后来才有这个问题。 -_-!!哭啊~~ 只能用一个方式规避这个问题,把被覆写变量的地址改为bdata段地址。修改后暂未发现程序中其他变量被覆写的问题。
调试的效果图

贴出第一次写51代码^^。小弟有一点开发经验,只是软硬结合的这种模式属于第一个。
--------------------------------
#include <STC12C5A60S2.h>
#include "Pins.h"
#include "LEDDisplay.h"
#include "Temperature.h"
#include "Relays.h"
#include "PilotLamp.h"
#define uchar unsigned char
#define uint unsigned int
// 获取温度的时间间隔
#define TEMPGETCOUNT 5000 // 温度读取间隔, 500ms 读一次温度,即1秒读2次温度。
#define CHANGINGCOUNT 20000 // 温度调控状态时会显示调控温度值,最长显示时间。
#define CRITICALCOUNTDOWN 5 // 温度下临界跳变阀值
#define CRITICALCOUNTUP 5 // 温度上临界跳变阀值
#define CRITICALCOUNTERR 3 // 温度错误,3次关闭
#define DEFAULTCRITICALTEMP 2500 // 默认跳变温度 25°C
#define VALIDTEMPERATURE_MAX 3000 // 最大有效温度 30°C
#define VALIDTEMPERATURE_MIN 2000 // 最小有效温度 20°C
#define TEMPERATURESPET 50 // 0.5 °C 温度调整步长
#define COOL_POWERALL 0xFF // 全力运行
#define COOL_MALAISE 0x00 // 萎靡~~~~~ 哇咔咔~~~
#define KEYPRESS_LONG 200 // 按键长按计数,如一只按住不放
#define KEYPRESS_SHORT 15 // 短按计数
#define FOSC 1843200L
#define T1MS (65536-FOSC/12/1000) // 计时器工作频率 12T状态
uint CriticalTempVal; // 临界温度,跳变温度
uint RealTempVal; // 当前温度,获取一定次数时都
uint ChangeCount; // 临界切换计数
uchar RelayVal; // 继电器状态控制位 从右到左 0x03(0000 0011) 那么第一第二个继电器打开。
// 指示灯状态
// 第一位 继电器工作状态,灯亮工作状态,灯灭停止制冷
// 第二位 是否为自动模式
// 第三位 温控器故障
// 0000 0111
uchar DirectLamp;
uchar bdata WorkStatus;
sbit InCritical = WorkStatus ^ 0; // 临界状态
sbit InWorking = WorkStatus ^ 1; // 工作状态
sbit InAuto = WorkStatus ^ 2; // 自动模式
sbit InHandOn = WorkStatus ^ 3; // 手动打开状态
sbit CanReadTemp = WorkStatus ^ 4; // 是否允许读取温度
sbit InInitTemp = WorkStatus ^ 5; // 温度读取是否在初始状态,解决第一次读取出现85°C的问题
sbit InChangingTemp = WorkStatus ^ 6; // 在温度调整中
// 温度读取时间间隔,减少18B20的读取次数,
// 目的是为数码管能获得比较高的刷新频率,提高显示亮度。
// 每次读取会有固定占用时间,能看出每次读取会有变暗的闪烁情况。
uint TempTimerSpace;
uint TempChangingStatusSpace; // 温度调控状态中
void Init(void);
bit CanUpdateTemp(void);
void UpdateTemperature(void);
void ReadTemperature(void);
void UpdateStatus(void);
void InitTimer(void);
void SearchKeys(void);
unsigned int GetCriticalTemperature(void);
//void SaveCriticalTemperature(unsigned int);
// 按键
// 18 K3 17 K4 16 K5
sbit Key_Mode = P1 ^ 1;
sbit Key_CTDown = P1 ^ 0;
sbit Key_CTUp = P0 ^ 0;
//enum KeyType (TNone=0, KTMode=1, KTCTDown=2, KTCTUp=4);
uchar KeyCount;
uchar KeyTypeVal;
#define KEYTYPE_NONE 0 // 没有按键使用
#define KEYTYPE_MODE 1 // 按下模式切换状态
#define KEYTYPE_CTDOWN 2 // 按下温度切换状态
#define KEYTYPE_CTUP 4 // 按下温度切换状态
void main()
{
Init();
while(1){
if (CanReadTemp)
ReadTemperature();
SearchKeys();
UpdateStatus();
UpdateRelays(RelayVal);
if (!InChangingTemp)
DisplayTemperature(RealTempVal);
else
DisplayTemperature(CriticalTempVal);
DisplayPilotLamp(DirectLamp);
}
}
void Init(void){
WorkStatus = 0x00;
InAuto = 1; // 默认自动模式
KeyCount = 0;
KeyTypeVal = KEYTYPE_NONE;
RealTempVal = DEFAULTCRITICALTEMP;
CriticalTempVal = GetCriticalTemperature();
InitTimer();
InitPilotLamp();
InitView();
InitTemperature();
InitRealys();
}
void SearchKeys(void){
// 模式切换
if (Key_Mode == 0){
KeyTypeVal = KEYTYPE_MODE;
KeyCount++;
}
// 温控温度
if (Key_CTDown == 0) {
KeyTypeVal = KEYTYPE_CTDOWN;
KeyCount ++;
// 长按状态
if (KeyCount > KEYPRESS_LONG) {
KeyCount = 0;
if (CriticalTempVal > VALIDTEMPERATURE_MIN) {
TempChangingStatusSpace = CHANGINGCOUNT;
CriticalTempVal -= TEMPERATURESPET;
}
}
}
// 温控温度
if (Key_CTUp == 0) {
KeyTypeVal = KEYTYPE_CTUP;
KeyCount ++;
// 长按状态
if (KeyCount > KEYPRESS_LONG) {
KeyCount = 0;
if (CriticalTempVal < VALIDTEMPERATURE_MAX){
TempChangingStatusSpace = CHANGINGCOUNT;
CriticalTempVal += TEMPERATURESPET;
}
}
}
if (KeyCount > KEYPRESS_SHORT){
if (Key_Mode == 1 && KeyTypeVal == KEYTYPE_MODE){
KeyTypeVal = KEYTYPE_NONE;
KeyCount = 0;
if (InAuto){
InAuto = 0;
InHandOn = 1;
}
else {
if (InHandOn)
InHandOn = 0;
else
InAuto = 1;
}
}
if (Key_CTDown == 1 && KeyTypeVal == KEYTYPE_CTDOWN){
KeyTypeVal = KEYTYPE_NONE;
KeyCount = 0;
if (CriticalTempVal > VALIDTEMPERATURE_MIN) {
CriticalTempVal -= TEMPERATURESPET;
TempChangingStatusSpace = CHANGINGCOUNT;
}
}
if (Key_CTUp == 1 && KeyTypeVal == KEYTYPE_CTUP){
KeyTypeVal = KEYTYPE_NONE;
KeyCount = 0;
if (CriticalTempVal < VALIDTEMPERATURE_MAX) {
TempChangingStatusSpace = CHANGINGCOUNT;
CriticalTempVal += TEMPERATURESPET;
}
}
}
}
void ReadTemperature(void){
unsigned int Val;
Val = GetTemperature();
// 18B20有个特殊问题,第一次读取会出现 85°C
if (InInitTemp && Val == 8500){
CanReadTemp = 1;
return;
}
RealTempVal = Val;
CanReadTemp = 0;
InInitTemp = 0;
//
// 对于临界温度,需要特殊处理。
// 防止温控在临界时频繁跳变,当在临界一侧温度超缓冲量时才允许跳转模式。
// 当温控探头无效时优先处理
//
// 如果温控探头被拔出,再次插入的时候会出现 85°C的错误情况
// 只要温度读取错误,那么就认为温控探头是被拔出状态。
//
if (RealTempVal == VAL_ERRTEMPERATURE){
InInitTemp = 1;
if (InCritical)
ChangeCount--;
else {
InCritical = 1;
ChangeCount = CRITICALCOUNTERR;
}
}
else if (InWorking) {
if (RealTempVal < CriticalTempVal){
if (InCritical)
ChangeCount--;
else {
InCritical = 1;
ChangeCount = CRITICALCOUNTDOWN;
}
}
else {
InCritical = 0;
ChangeCount = 0;
}
}
else {
if (RealTempVal > CriticalTempVal){
if (InCritical)
ChangeCount --;
else {
InCritical = 1;
ChangeCount = CRITICALCOUNTUP;
}
}
else {
InCritical = 0;
ChangeCount = 0;
}
}
}
void InitTimer(void){
// 使用定时器1作为时间计数
TMOD = 0x01;
TL0 = T1MS;
TH0 = T1MS >> 8;
TR0 = 1;
ET0 = 1;
EA = 1;
CanReadTemp = 0;
TempTimerSpace = TEMPGETCOUNT; // 第一次温度读取
InInitTemp = 1; // 温度处于初始状态,解决85°C问题
TempChangingStatusSpace = 0; // 不在调温状态
}
void UpdateStatus(void){
//
// 温控临界跳变, 缓冲计数为零时跳变
//
//
if (InCritical && !ChangeCount){
InCritical = 0;
if ((RealTempVal != VAL_ERRTEMPERATURE) && (RealTempVal > CriticalTempVal))
InWorking = 1;
else
InWorking = 0;
}
// 是否在调温状态
if (TempChangingStatusSpace)
InChangingTemp = 1;
else
InChangingTemp = 0;
//
// 温控状态和手工状态
// 温控状态时根据是否工作状态判断,手动模式下通过是否强行开启判断继电器模式
//
if((InAuto && InWorking) || (!InAuto && InHandOn))
RelayVal = COOL_POWERALL;
else
RelayVal = COOL_MALAISE;
// 工作状态信息更新
if (RelayVal > 0)
DirectLamp = 1;
else
DirectLamp = 0;
if (InAuto)
DirectLamp |= 0x02;
}
unsigned int GetCriticalTemperature(void){
//todo : 这里的值需要从EEPROM中获取
return (DEFAULTCRITICALTEMP);
}
/*
void SaveCriticalTemperature(unsigned int Val){
//todo : 保存临界温度到EEPROM,防止停电丢失数据
}
*/
void time0(void) interrupt 1{
// 时钟
TL0 = T1MS;
TH0 = T1MS >> 8;
TempTimerSpace--;
if (!TempTimerSpace){
CanReadTemp = 1;
TempTimerSpace = TEMPGETCOUNT;
}
if (TempChangingStatusSpace)
TempChangingStatusSpace--;
}
|
|