|
本帖最后由 WUWEWU 于 2023-4-1 11:44 编辑
程序是用STC89C52单片机,用12864 OLED显示屏显示的温控器代码,带温度设定-20-150度 及PID参数设定,上下限超温报警输出,按键4颗,(1 设定(长按5秒进入),2 换行按键,3 加键 4 减键),重新检查确定无误 再写一份好吗?
原程序如下
#include <STC89C5xRC.H>
#define LCD_DB P0
#define LCD_RS P2_4
#define LCD_EN P2_5
unsigned char kp,kp_r; //kp记录现在按键的状态,kp_r用来记录前一时刻按键状态,互锁
unsigned char cnt,flag; //cnt是计数器,flag是标志位
unsigned int set_T,now_T,P; //set_T是设定温度,now_T是当前温度,P是PID算法计算出的控制量
sbit beep = P1^5; //超温报警蜂鸣器
/**********************************************
函数名:LCD_delay_us()
功 能: us级延迟函数
实现过程: 连续5次不进行循环控制体现延时的稳定性
入口参数: nus,需要的us数
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_delay_us(unsigned char nus) //晶体管液晶显示屏延时函数,单位为微秒
{
unsigned char i;
while (nus--)
{
i = 12;
while (--i);
}
}
/**********************************************
函数名:LCD_write_com()
功 能: 向LCD写入指令,其他关于位逻辑操作的地方与之类似
实现过程: 总线操作,往寄存器中写数据,使能拉高触发上升沿
入口参数: com,要写入的指令值
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_write_com(unsigned char com)
{
LCD_RS = 0; //RS=0,写入指令
LCD_DB = com; //写入指令
LCD_EN = 1; //使能拉高
LCD_delay_us(2); //延时2us
LCD_EN = 0; //使能拉低
}
/**********************************************
函数名:LCD_write_data()
功 能: 向LCD写入数据
实现过程: 总线操作,往寄存器中写数据,使能拉高触发上升沿
入口参数: dat,要写入的数据值
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_write_data(unsigned char dat)
{
LCD_RS = 1; //RS=1,写入数据
LCD_DB = dat; //写入数据
LCD_EN = 1; //使能拉高
LCD_delay_us(2); //延时2us
LCD_EN = 0; //使能拉低
}
/**********************************************
函数名:LCD_init()
功 能: 初始化LCD显示屏
实现过程: 依次写入指令,实现初始化
入口参数: 无
出口参数: 无
作 者 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_init(void)
{
LCD_write_com(0x38); //8位数据总线,2行显示,5x7点阵字符
LCD_write_com(0x0c); //显示开,光标关,光标闪烁关
LCD_write_com(0x06); //文字不动,地址自动加1
LCD_write_com(0x01); //清屏
}
/**********************************************
函数名:LCD_show_char()
功 能: 在LCD上显示一个字符
实现过程: 先写入地址,再写入数据
入口参数: x,y,字符的位置;dat,要显示的字符
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_show_char(unsigned char x,unsigned char y,unsigned char dat)
{
unsigned char addr;
if (y == 0) addr = 0x80 + x; //第一行
else addr = 0xc0 + x; //第二行
LCD_write_com(addr); //写入地址
LCD_write_data(dat); //写入数据
}
/**********************************************
函数名:LCD_show_str()
功 能: 在LCD上显示一个字符串
实现过程: 循环调用LCD_show_char()函数
入口参数: x,y,字符串的位置;*str,要显示的字符串
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_show_str(unsigned char x,unsigned char y,unsigned char *str)
{
while (*str != '\0')
{
LCD_show_char(x++,y,*str++);
}
}
/**********************************************
函数名:key_scan()
功 能: 按键扫描函数
实现过程: 通过P3口读取按键状态,判断按键是否按下
入口参数: 无
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void key_scan(void)
{
kp_r = kp; //记录前一时刻按键状态
kp = P3; //读取按键状态
if (kp != kp_r) //如果按键状态发生改变
{
cnt = 0; //计数器清零
if (kp == 0x0f) flag = 0; //如果所有按键都没有按下,标志位清零
}
else
{
cnt++; //计数器加1
if (cnt == 20) //如果计数器达到20,说明按键已经按下超过100ms
{
flag = 1; //标志位置1
cnt = 0; //计数器清零
}
}
}
/**********************************************
函数名:beep_alarm()
功 能: 超温报警函数
实现过程: 蜂鸣器发出声音
入口参数: 无
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void beep_alarm(void)
{
beep = 1; //蜂鸣器拉高
delay_ms(100); //延时100ms
beep = 0; //蜂鸣器拉低
delay_ms(100); //延时100ms
}
/**********************************************
函数名:PID_control()
功 能: PID控制函数
实现过程: 根据当前温度和设定温度计算出控制量P
入口参数: 无
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void PID_control(void)
{
float Kp = 1.0, Ki = 0.1, Kd = 0.01; //PID参数
static float err_sum = 0, last_err = 0; //误差累计和,上一次误差
float err = set_T - now_T; //当前误差
err_sum += err; //误差累计和
P = Kp * err + Ki * err_sum + Kd * (err - last_err); //PID控制量
last_err = err; //更新上一次误差
}
/**********************************************
函数名:main()
功 能: 主函数
实现过程: 初始化,循环读取温度并控制温度
入口参数: 无
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void main(void)
{
LCD_init(); //LCD初始化
while (1)
{
key_scan(); //按键扫描
if (flag == 1) //如果有按键按下
{
if ((kp & 0x01) == 0) //如果第一颗按键按下
{
set_T--; //设定温度减1
if (set_T < -20) set_T = -20; //设定温度下限为-20度
}
if ((kp & 0x02) == 0) //如果第二颗按键按下
{
set_T++; //设定温度加1
if (set_T > 150) set_T = 150; //设定温度上限为150度
}
if ((kp & 0x04) == 0) //如果第三颗按键按下
{
//进入PID参数设定模式
}
if ((kp & 0x08) ==
*****以下是chatGPT修改过的*****
//向LCD写入指令
void LCD_write_com(unsigned char com)
{
LCD_RS = 0; //RS置0,选择指令寄存器
LCD_DB = com; //写入指令
LCD_EN = 1; //使能拉高
LCD_delay_us(2); //延时
LCD_EN = 0; //使能拉低
}
/**********************************************
函数名:LCD_write_data()
功 能: 向LCD写入数据
实现过程: 总线操作,往寄存器中写数据,使能拉高触发上升沿
入口参数: dat,要写入的数据值
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_write_data(unsigned char dat)
{
LCD_RS = 1; //RS置1,选择数据寄存器
LCD_DB = dat; //写入数据
LCD_EN = 1; //使能拉高
LCD_delay_us(2); //延时
LCD_EN = 0; //使能拉低
}
/**********************************************
函数名:LCD_init()
功 能: 初始化LCD
实现过程: 按照LCD的初始化流程,写入指令
入口参数: 无
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_init()
{
LCD_write_com(0x38); //8位数据总线,2行显示,5x7点阵字符
LCD_write_com(0x0c); //开显示,关光标,不闪烁
LCD_write_com(0x06); //光标右移,字符不移动
LCD_write_com(0x01); //清屏
}
/**********************************************
函数名:LCD_display()
功 能: 在LCD上显示字符串
实现过程: 循环写入每个字符
入口参数: str,要显示的字符串
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_display(unsigned char *str)
{
while (*str)
{
LCD_write_data(*str++);
}
}
/**********************************************
函数名:key_scan()
功 能: 按键扫描函数
实现过程: 通过检测按键的电平变化,判断按键是否按下
入口参数: 无
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void key_scan()
{
kp_r = kp; //记录前一时刻按键状态
if (P3_0 == 0) //检测按键1是否按下
{
kp = 1;
}
else if (P3_1 == 0) //检测按键2是否按下
{
kp = 2;
}
else if (P3_2 == 0) //检测按键3是否按下
{
kp = 3;
}
else if (P3_3 == 0) //检测按键4是否按下
{
kp = 4;
}
else
{
kp = 0;
}
}
/**********************************************
函数名:PID()
功 能: PID算法计算控制量
实现过程: 根据设定温度和当前温度计算出PID控制量
入口参数: 无
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void PID()
{
int err, err_last, integral, derivative; //误差、上一时刻误差、积分、微分
static int integral_sum = 0; //积分累加器
err = set_T - now_T; //计算误差
integral_sum += err; //累加误差
if (integral_sum > 1000) //积分限幅
{
integral_sum = 1000;
}
else if (integral_sum < -1000)
{
integral_sum = -1000;
}
integral = integral_sum; //积分项
derivative = err - err_last; //微分项
P = 10 * err + 2 * integral + 5 * derivative; //PID控制量
err_last = err; //记录上一时刻误差
}
/**********************************************
函数名:main()
功 能: 主函数
实现过程: 初始化,循环检测按键,采集温度,计算PID控制量,控制蜂鸣器报警,显示温度和PID控制量
入口参数: 无
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void main()
{
TMOD = 0x01; //定时器0工作在模式1
TH0 = 0xfc; //定时器0初值,1ms
TL0 = 0x18;
TR0 = 1; //启动定时器0
ET0 = 1; //开定时器0中断
EA = 1; //开总中断
LCD_init(); //初始化LCD
set_T = 25; //设定温度为25度
while (1)
{
key_scan(); //按键扫描
if (kp_r == 0 && kp == 1) //按键1按下
{
flag = 1; //标志位置1
cnt = 0; //计数器清零
}
if (flag == 1 && kp == 1) //按键1持续按下
{
cnt++; //计数器累加
if (cnt == 100) //长按5秒
{
cnt = 0; //计数器清零
flag = 0; //标志位置0
LCD_write_com(0x01); //清屏
LCD_display("Set Temp:"); //显示设定温度
while (1)
{
key_scan(); //按键扫描
if (kp_r == 0 && kp == 2) //按键2按下
{
set_T++; //设定温度加1
if (set_T > 150) //温度上限
{
set_T = 150;
}
}
if (kp_r == 0 && kp == 3) //按键3按下
{
set_T--; //设定温度减1
if (set_T < -20) //温度下限
{
set_T = -20;
}
}
if (kp_r == 0 && kp == 4) //按键4按下
{
flag = 1; //标志位置1
cnt = 0; //计数器清零
break; //退出设定温度模式
}
LCD_write_com(0xc0); //光标移到第二行第一列
LCD_display(" "); //清空显示
LCD_write_com(0xc0); //光标移到第二行第一列
LCD_display("Set Temp:"); //显示设定温度
LCD_write_data(set_T / 10 + '0'); //显示设定温度的十位数
LCD_write_data(set_T % 10 + '0'); //显示设定温度的个位数
}
}
}
else if (flag == 1 && kp != 1) //按键1松开
{
flag = 0; //标志位置0
cnt = 0; //计数器清零
}
if (now_T > set_T + 5 || now_T < set_T - 5) //超温报警
{
beep = 1; //蜂鸣器报警
}
else
{
beep = 0; //蜂鸣器停止报警
}
LCD_write_com(0x80); //光标移到第一行第一列
LCD_display("Now Temp:"); //显示当前温度
LCD_write_data(now_T / 10 + '0'); //显示当前温度的十位数
LCD_write_data(now_T % 10 + '0'); //显示当前温度的个位数
LCD_display(" Set Temp:"); //显示设定温度
再次修改整理后
****/
void LCD_write_com(unsigned char com) //写命令函数
{
LCD_RS = 0; //RS置0
LCD_DB = com; //写入数据
LCD_EN = 1; //使能置1
LCD_delay_us(1); //延迟1us
LCD_EN = 0; //使能置0
}
/**********************************************
函数名:LCD_write_data()
功 能: 向LCD写入数据
实现过程: 总线操作,往寄存器中写数据,使能拉高触发上升沿
入口参数: dat,写入的数据
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_write_data(unsigned char dat) //写数据函数
{
LCD_RS = 1; //RS置1
LCD_DB = dat; //写入数据
LCD_EN = 1; //使能置1
LCD_delay_us(1); //延迟1us
LCD_EN = 0; //使能置0
}
/**********************************************
函数名:LCD_init()
功 能: 初始化LCD
实现过程:
入口参数: 无
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_init(void) //总初始化函数
{
LCD_write_com(0x38); //功能设定
LCD_write_com(0x38); //功能设定
LCD_write_com(0x38); //功能设定
LCD_write_com(0x08); //关闭显示
LCD_write_com(0x01); //清屏
LCD_write_com(0x06); //光标不加,整屏左移
LCD_write_com(0x0f); //开显示,有光标,光标闪烁
}
/**********************************************
函数名:LCD_display_one_char()
功 能: 在LCD上显示一个字符
实现过程: 调用LCD_write_data()函数
入口参数: x,要显示字符的行;y,要显示字符的列;dat,要显示的字符
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_display_one_char(unsigned char x, unsigned char y, unsigned char dat)
{
unsigned char address;
if (x == 0) //确定地址
address = 0x80 + y;
else if (x == 1)
address = 0xc0 + y;
else if (x == 2)
address = 0x94 + y;
else
address = 0xd4 + y;
LCD_write_com(address); //写地址
LCD_write_data(dat); //写字符
}
/**********************************************
函数名:LCD_display_string()
功 能: 在LCD上显示一个字符串
实现过程: 调用LCD_display_one_char()函数
入口参数: x,要显示字符串的行;y,要显示字符串的列;*dat,指向要显示的字符串的指针
出口参数: 无
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
void LCD_display_string(unsigned char x, unsigned char y, unsigned char *dat)
{
while (*dat)
{
LCD_display_one_char(x, y++, *dat++);
}
}
/**********************************************
函数名:key_scan()
功 能: 按键扫描函数,根据按键情况返回相应值
实现过程: 根据电路图写代码
入口参数: 无
出口参数: 返回按键值,0表示无按键,1表示设定键,2表示换行键,3表示加键,4表示减键
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
unsigned char key_scan(void) //按键扫描
{
unsigned char value = 0;
kp = P2 & 0x0f; //读取按键值
if (kp != kp_r) //判断是否有按键按下
{
cnt = 0;
flag = 0; //计数器计数器清0
}
else
{
if (++cnt > 100)
{
cnt = 0;
flag = 1; //计数器溢出,标志位置1
}
}
if (flag && kp) //如果计数器溢出,且当前有按键按下
{
flag = 0;
value = kp; //返回按键值
}
kp_r = kp; //记录当前按键状态
return value;
}
/**********************************************
函数名:get_one_temperature()
功 能: 读取一次温度值
实现过程: 循环读取温度传感器,返回温度值
入口参数: 无
出口参数: 返回读取到的温度值
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
unsigned char get_one_temperature() //读取一次温度函数
{
unsigned char temp_H, temp_L;
unsigned int temperature;
//开始转换
P1 = 0xfe; //拉低DS18B20总线
LCD_delay_us(1); //延迟1us
P1 = 0; //释放总线
while (P1 & 0x01); //等待DS18B20回应
LCD_delay_us(30); //延迟30us
P1 = 0xfe; //拉低DS18B20总线
LCD_delay_us(1); //延迟1us
P1 = 0; //释放总线
while (P1 & 0x01); //等待DS18B20回应
LCD_delay_us(100); //延迟100us
//开始读取温度值
P1 = 0xfe; //拉低DS18B20总线
LCD_delay_us(1); //延迟1us
P1 = 0; //释放总线
while (P1 & 0x01); //等待DS18B20回应
LCD_delay_us(30); //延迟30us
temp_L=P1; //读取温度低8位
P1 = 0xfe; //拉低DS18B20总线
LCD_delay_us(1); //延迟1us
P1 = 0; //释放总线
while (P1 & 0x01); //等待DS18B20回应
LCD_delay_us(30); //延迟30us
temp_H=P1; //读取温度高8位
//计算温度值
temperature = ((unsigned int)temp_H) << 8;
temperature |= temp_L;
temperature >>= 4;
return (unsigned char)temperature;
}
/**********************************************
函数名:MatrixK()
功 能: 计算Matrix的逆矩阵
实现过程: 矩阵运算公式实现
入口参数: *in,输入矩阵;*out,输出矩阵
出口参数: 1表示Matrix可逆,0表示Matrix不可逆
作 者: 吴好862210674@qq.com
修改日志:
************************************************/
unsigned char MatrixK(double *in, double *out) //计算Matrix的逆矩阵
{
double A[5][5], B[5][5];
double t, p;
unsigned char i, j, m, k;
//将一维数组in转换为二维数组A
for (i = 0; i < 5; i++)
for (j = 0; j < 5; j++)
A[j] = *(in + i * 5 + j);
//将B设为单位矩阵
for (i = 0; i < 5; i++)
for (j = 0; j < 5; j++)
B[j] = (i == j) ? 1.0 : 0.0;
//高斯消元法求B的逆矩阵
for (k = 0; k < 5; k++)
{
t = 1.0 / A[k][k];
A[k][k] = 1.0;
for (j = 0; j < 5; j++)
B[k][j] *= t;
for (i = k + 1; i < 5; i++)
{
t = A[i |
阿莫论坛20周年了!感谢大家的支持与爱护!!
你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。
|