/**

数码之家 c106czs 编写于 2020年3月13日

请勿用于商业用途!

*/

#include

#include

#include

sbit t12 = P3 ^ 7;         //T12控制

sbit encoderb = P1 ^ 0;    //编码器的b脚

sbit encodera = P1 ^ 1;    //编码器的a脚

sbit encoderd = P1 ^ 2;    //编码器的按键d脚

sbit DIO = P3 ^ 3;         // TM1650 数码管驱动的sda引脚

sbit CLK = P3 ^ 2;         // TM1650 数码管驱动的scl引脚

sbit DO = P5 ^ 5;          //DS18B20数据脚

unsigned long VREF = 2390; // 用万用表测量基准电压的真实值,单位mv

bit lastb = 0;

bit lasta = 0;

unsigned short push_last_time = 0; //记录按下编码器按钮的时间,短按和长按

unsigned long t12_voltage;    // 计算t12热电偶电压

unsigned long system_voltage; // 计算单片机供电电压

unsigned long input_voltage;  // 计算整个板子的输入电压(12~24V)

// PID控制算法

#define KP 1.2f       // 比例系数

#define KI 0.2f       // 积分系数

#define KD 0.1f       // 微分系数

#define MAX_UK 400.0f // 系统允许输出的最大控制量,这里表现为加热数,400个0.5ms加热周期,最长连续加热时间为200ms

int ek = 0, ek_1 = 0, ek_2 = 0; // 记录连续三次的偏差值(设定值-实际测量值)

float uk_1 = 0.0f, uk = 0.0f;   // 记录当前计算的PID调整值和上次计算的PID调整值

long integralSum = 0;           // 位置式PID算法的累计积分项

// 定义一个数码管段码表,0~F

unsigned char CODE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0X7C, 0X39, 0X5E, 0X79, 0X71};

unsigned int t12SetTemperature = 10;   // 记录当前设定的温度

unsigned int t12ActualTemperature = 0; // 保存T12当前的实际温度

bit isChangeTemperature = 0;           // 标记是否更改过设定温度

unsigned int setTempShowTime = 0;      // 记录显示设置温度的时长

unsigned int need_heat_time = 0;       // 需要加热的时长

unsigned int heat_time_count = 0;      //当前已经加热的时长

unsigned int actualTempShowTime = 250; //设定当前温度的显示时长,避免采集温度过快造成数码管乱跳

/*延时函数,使用STC-ISP自动生成的,比较准确*/

void Delay_ms(unsigned int k) //@12.000MHz

{

unsigned char i, j;

for (; k > 0; k--)

{

i = 12;

j = 169;

do

{

while (--j)

;

} while (--i);

}

}

void Delay_us(unsigned int i)

{

for (; i > 0; i--)

{

_nop_();

_nop_();

_nop_();

_nop_();

}

}

// 初始化各个IO口

void initIO()

{

// 配置各个端口的输入模式,M1M0:00普通,01推挽,10高阻输入,11开漏

/*

以下列出需要配置的端口,其他端口保持默认即可

t12 = P3^7;             //T12控制  推挽输出模式

ADC3:系统输入电压检测 P1^3 输入模式

ADC4:T12热电偶电压检测 P1^4 输入模式

ADC5:2.5V参考电压输入 P1^5 输入模式

*/

//P1M0 |= 0x00; //0000 0000

P1M1 |= 0x38; //0011 1000

P3M0 = 0x80; //1000 0000

P3M1 = 0x00; //0000 0000

}

/*初始化ADC*/

void initADC(void)

{

/*

开启相应ADC口的模拟输入功能(相应位置1)

ADC3:系统输入电压检测 P1^3

ADC4:T12热电偶电压检测 P1^4

ADC5:2.5V参考电压输入 P1^5

*/

P1ASF = 0x38; //0011 1000

ADC_RES = 0;  // 清楚结果寄存器

ADC_RESL = 0;

/*

ADC控制寄存器

ADC_POWER | SPEED1 | SPEED0 | ADC_FLAG | ADC_START | CHS2 | CHS1 | CHS0

*/

// 这里初始化的时候,可以先打开电源和设置转换速度

ADC_CONTR = 0x80; // 1000 0000

Delay_ms(5);      // 上电之后延时等待一段时间

}

// 关闭ADC电源,在进入空闲(休眠)模式的时候启用,降低功耗

void closeADC(void)

{

/*

ADC控制寄存器

ADC_POWER | SPEED1 | SPEED0 | ADC_FLAG | ADC_START | CHS2 | CHS1 | CHS0

*/

ADC_CONTR = 0x00;

}

// 直接插入排序

void insertionSort(unsigned int A[], unsigned int n)

{

unsigned int i, j;

for (i = 1; i < n; i++)

{

for (j = i; j > 0; j--)

{

if (A[j] < A[j - 1])

{

// 不使用第三变量交换两个数,使用异或运算速度快

A[j - 1] ^= A[j];

A[j] ^= A[j - 1];

A[j - 1] ^= A[j];

}

}

}

}

#define ADC_FLAG 0x10  // ADC转换完成标志

#define ADC_START 0x08 // ADC开始置位

// 获取某个ADC通道的转换值

// 为了提高结果的准确性,每次测量,测5次,并且去掉一个最高值,一个最低值,最后取中间3个的均值返回

unsigned int getADCResult(unsigned int channel)

{

unsigned int res[5], i, result = 0;

for (i = 0; i < 5; i++)

{

/*

ADC控制寄存器

ADC_POWER | SPEED1 | SPEED0 | ADC_FLAG | ADC_START | CHS2 | CHS1 | CHS0

*/

ADC_CONTR = (0x80 | channel | ADC_START); // 选择需要读取的通道,并开启转换

Delay_us(1);

while (!(ADC_CONTR & ADC_FLAG))

;                                            //等待ADC转换完成

res[i] = ((ADC_RES << 2) | (ADC_RESL & 0x03)); // 计算转换出来的原始结果

ADC_RES = 0x00;

ADC_RESL = 0x00; // 清零结果寄存器

}

// 对结果进行排序

insertionSort(res, 5);

// 去掉一个最高值,去掉一个最低值,剩余三个求平均值

result = (res[1] + res[2] + res[3]) / 3;

return result;

}

/*以下是计算各种电压的函数,返回结果的单位都是mv*/

// 计算公式(mv) output_voltage = (VREF万用表测的TL431基准电压值(mv) * 待测通道的ADC值 / TL431基准通道ADC值)

// 计算获取T12的电压

void getT12Voltage(void)

{

t12_voltage = (VREF * getADCResult(4) / getADCResult(5)); //计算t12电压,单位mV

}

// 计算获取单片机的电源电压

void getSystemVoltage(void)

{

system_voltage = (VREF * 1024 / getADCResult(5)); //计算单片机的电源电压,单位mV;

}

// 计算获取输入电源电压

void getInputVoltage(void)

{

input_voltage = (VREF * getADCResult(3) / getADCResult(5) * 11); // 计算下分压电阻点的电压,并乘分压比,获得实际的输入电压

}

/* 计算T12热电偶温度 */

void getT12Temperature(void)

{

float x = 0.0f;

t12 = 0;         //测温的时候,关闭电烙铁

Delay_us(10);    // 等待一段时间,等电容放完电后再测量温度比较准确

getT12Voltage(); // 更新T12热电偶电压

//T12实际温度 = (ADC电压(mV)-失调电压(mV))/运放增益*热电偶分度值(℃/mV)+室温(℃)

//t12Temp = 1.0f*getT12Voltage() / 231.0f * 54.0f + 23.0f;

// 插值函数计算T12温度,上面的算得不够准确,自己测量了t12温度与热电偶电动势的关系,用matlab拟合出来的公式

x = 1.0f * t12_voltage / 231.0f;

x = -0.9f * x * x + 48.0f * x + 22.0f;

t12ActualTemperature = (unsigned int)x;

}

/*

// 增量式PID控制算法,该算法用在温度控制效果不佳,调参调了比较久,不是很理想

// 输入设定温度和当前温度

// 返回当前应该加热的时长

void incrementalPID(unsigned int setTemperature, unsigned int actualTemperature)

{

float delta_uk = 0.0f;                   // 用于计算PID增量值

uk_1 = uk;                               // 记录上次计算的PID调整值

ek_2 = ek_1;                             // 记录上上次计算的偏差值

ek_1 = ek;                               // 记录上次计算的偏差值

ek = setTemperature - actualTemperature; // 计算当前偏差值

if (ek < 0)

{

// 如果实际温度比设定温度还要高,那么不执行加热

need_heat_time = 0;

return;

}

if (abs(ek) > 100)

{

// 如果温差大于100℃,则执行系统的动态加速,不管比例项为正还是为负,都取正数

delta_uk = KP * abs(ek - ek_1) + KI * ek + KD * (ek - 2 * ek_1 + ek_2); // 计算PID增量值

}

else

{

// 当快要接近目标温度的时候,执行正常的调节

delta_uk = KP * (ek - ek_1) + KI * ek + KD * (ek - 2 * ek_1 + ek_2); // 计算PID增量值

}

uk = uk_1 + delta_uk; // 计算当前应该输出的PWM值

// 判断是否超出了系统控制量的边界范围,如果超出,则赋值为边界

if (uk < 1e-9)

{

uk = 0.0f;

}

if (uk > MAX_UK)

{

uk = MAX_UK;

}

need_heat_time = (unsigned int)uk;

}

*/

// 位置式PID控制算法,这个控制算法运行起来比较理想

void positionalPID(unsigned int setTemperature, unsigned int actualTemperature)

{

ek_1 = ek;                               // 记录上次计算的偏差值

ek = setTemperature - actualTemperature; // 计算当前偏差值

if (ek < 0)

{

// 如果实际温度比设定温度还要高,那么不执行加热

need_heat_time = 0;

return;

}

// 当偏差较大时,取消积分作用

if (abs(ek) > 100)

{

integralSum = 0;

}

else

{

// 否则,根据情况进行累计积分

if (integralSum > 100) //积分超过上限时,只累计负的积分量

{

if (ek < 0)

{

integralSum += ek;

}

}

else if (integralSum < -10) //积分超过下限时,只累计正的积分量

{

if (ek > 0)

{

integralSum += ek;

}

}

else

{

integralSum += ek;

}

}

uk = KP * ek + KI * integralSum + KD * (ek - ek_1); // 计算当前应该输出的控制量值

// 判断是否超出了系统控制量的边界范围,如果超出,则赋值为边界

if (uk < 1e-9)

{

uk = 0.0f;

}

if (uk > MAX_UK)

{

uk = MAX_UK;

}

need_heat_time = (unsigned int)uk; // 更新当前需要加热的时间数

}

/********************************以下是TM1650数码管显示相关的函数****************************************************/

void Start1650(void)

{ //开始信号

CLK = 1;

DIO = 1;

Delay_us(5);

DIO = 0;

Delay_us(5);

DIO = 0;

}

void Ask1650(void)

{ //ACK信号

unsigned char timeout = 1;

CLK = 1;

Delay_us(5);

CLK = 0;

while ((DIO) && (timeout <= 100))

{

timeout++;

}

Delay_us(5);

CLK = 0;

}

void Stop1650(void)

{ //停止信号

CLK = 1;

DIO = 0;

Delay_us(5);

DIO = 1;

Delay_us(5);

}

void WrByte1650(unsigned char oneByte)

{ //写一个字节,高位在前,低位在后

unsigned char i;

CLK = 0;

Delay_us(1);

for (i = 0; i < 8; i++)

{

oneByte = oneByte << 1;

DIO = CY;

CLK = 0;

Delay_us(5);

CLK = 1;

Delay_us(5);

CLK = 0;

}

}

/*

unsigned char Scan_Key(void)

{ // 按键扫描

unsigned char i;

unsigned char rekey;

Start1650();

WrByte1650(0x49); //读按键命令

Ask1650();

//DIO = 1;

for (i = 0; i < 8; i++)

{

CLK = 1;

rekey = rekey << 1;

if (DIO)

{

rekey++;

}

Delay_us(5);

CLK = 0;

}

Ask1650();

Stop1650();

return (rekey);

}

*/

void Set1650(unsigned char add, unsigned char dat)

{ //数码管显示

//写显存必须从高地址开始写

Start1650();

WrByte1650(add); //第一个显存地址

Ask1650();

WrByte1650(dat);

Ask1650();

Stop1650();

}

// 初始化1650,传入亮度参数,范围0~7

void init1650(unsigned char light)

{

Set1650(0x48, ((light << 4) | 0x01));

}

// 数码管显示函数

void display(signed int num)

{

// 计算这个数字对应的千百十个位的数字

unsigned int tmpnum, qian, bai, shi, ge;

if (num < 0)

num = -num;

tmpnum = num;

qian = tmpnum / 1000;

tmpnum %= 1000;

bai = tmpnum / 100;

tmpnum %= 100;

shi = tmpnum / 10;

ge = tmpnum % 10;

Set1650(0x68, CODE[qian]);

Set1650(0x6A, CODE[bai]);

Set1650(0x6C, CODE[shi]);

Set1650(0x6E, CODE[ge]);

}

/************************************************************************************************

函数名称:Encoder

函数功能:编码器旋转的扫描及处理

入口参数:无

出口参数:char型    0-无旋转   'R'-正转(向右转)   'L'-反转(向左转)

************************************************************************************************/

unsigned char Encoder(void)

{

static bit Enc0 = 0;

static unsigned char EncOld, EncX = 0;

unsigned char EncNow;

encodera = 1; //PINA置高电平

encoderb = 1; //PINB置高电平

if (Enc0 == 0)

{

EncOld = (encodera ? 0x02 : 0x00) + (encoderb ? 0x01 : 0x00);

Enc0 = 1; //记住初次调用时编码器的状态

}

EncNow = (encodera ? 0x02 : 0x00) + (encoderb ? 0x01 : 0x00); //根据两个IO当前状态组合成16进制的0x00|0x01|0x02|0x03

if (EncNow == EncOld)

return (0); //如果新数据和原来的数据一样(没有转动)就直接返回0

if (EncOld == 0x00 && EncNow == 0x02 || EncOld == 0x03 && EncNow == 0x01)

EncX = EncNow;                                                                                          //00-10|11-01

if (EncOld == 0x00 && EncX == 0x02 && EncNow == 0x03 || EncOld == 0x03 && EncX == 0x01 && EncNow == 0x00) //00-10-11|11-01-00右转

{

EncOld = EncNow, EncX = 0;

return ('R'); //两定位一脉冲

}

if (EncOld == 0x00 && EncNow == 0x01 || EncOld == 0x03 && EncNow == 0x02)

EncX = EncNow;                                                                                          //00-01|11-10

if (EncOld == 0x00 && EncX == 0x01 && EncNow == 0x03 || EncOld == 0x03 && EncX == 0x02 && EncNow == 0x00) //00-01-11|11-10-00左转

{

EncOld = EncNow;

EncX = 0;

return ('L'); //两定位一脉冲

}

return (0); //没有正确解码返回0

}

// 编码器函数,用于设置t12的设定温度

void bianmaqi()

{

unsigned char enc;

enc = Encoder(); //扫描编码器,取得返回值

if (enc == 'R')

{

t12SetTemperature += 5;

if (t12SetTemperature > 500)

t12SetTemperature = 500;

isChangeTemperature = 1;

}

else if (enc == 'L')

{

t12SetTemperature -= 5;

if (t12SetTemperature < 10)

t12SetTemperature = 10;

isChangeTemperature = 1;

}

while (!encoderd)

{

push_last_time++;

Delay_ms(100);

}

if (push_last_time > 0 && push_last_time < 8)

{

push_last_time = 0;

Delay_ms(80); // 等待延时一段时间,看看有没有第二次按下

while (!encoderd)

{

push_last_time++;

Delay_ms(100);

}

if (push_last_time > 0 && push_last_time < 8)

{

// 双击了编码器按钮,关闭电烙铁输出

t12SetTemperature = 10;

t12 = 0;

isChangeTemperature = 1;

}

else

{

// 短按一次编码器按钮,设定为300℃输出

t12SetTemperature = 300;

isChangeTemperature = 1;

}

}

else if (push_last_time >= 8)

{

// 长按编码器按钮,设定为250℃输出

t12SetTemperature = 250;

isChangeTemperature = 1;

}

if (isChangeTemperature)

{

// 如果有改变设置温度,那么就要显示新的设置温度

setTempShowTime = 600;

}

push_last_time = 0;

}

// 该定时器初始化函数部分使用STC-ISP下载软件生成,加入开启中断的寄存器赋值

void Timer0Init(void) //500微秒 0.5ms @12.000MHz

{

AUXR |= 0x80; //定时器时钟1T模式

TMOD &= 0xF0; //设置定时器模式

TL0 = 0x90;   //设置定时初值

TH0 = 0xE8;   //设置定时初值

TF0 = 0;      //清除TF0标志

ET0 = 1;      //开启定时器0中断

TR0 = 1;      //定时器0开始计时

EA = 1;       // CPU总中断允许控制位

}

//定时0中断函数,每隔0.5ms执行一次

void timer0(void) interrupt 1

{

bianmaqi(); // 调用编码器函数,获取编码器当前状态(左旋,右旋,短按,长按)

if (heat_time_count < need_heat_time)

{

// 如果当前还没完成需要加热的时长,就控制t12加热

t12 = 1;

heat_time_count++;

}

else

{

// 已经完成相应的加热时长,调用PID函数获取下一个需要的加热数

t12 = 0;

getT12Temperature();                                    // 获取当前T12的温度

positionalPID(t12SetTemperature, t12ActualTemperature); // 更新当前需要加热的时间计数

heat_time_count = 0;                                    //重新开始统计加热时长

}

if (setTempShowTime > 0)

{

// 还没显示够显示设置温度的时长,需要继续显示,这里用7开头表示显示设定温度

display(t12SetTemperature + 7000);

setTempShowTime--;

// 已经显示了设置温度了,那么要把这个更改过设置温度的标志开关关闭

isChangeTemperature = 0;

}

else

{

if (actualTempShowTime < 2)

{

display(t12ActualTemperature); // 显示当前的t12实际温度

actualTempShowTime = 250;

}

actualTempShowTime--;

}

}

void main()

{

initIO(); // 初始化IO口

t12 = 0;

init1650(1); // 初始化TM1650显示

initADC();   // 初始化ADC

getSystemVoltage(); //检测单片机电源电压

getInputVoltage();  //检测输入电压

// 显示输入电压,这里的7123和7456仅仅只是用于标识显示什么内容,毕竟没有OLED屏幕这么高大上可以显示很多内容

display(7123);

Delay_ms(1000);

display(input_voltage / 10); // 最大输入电压24000mv, 只有4位数码管

Delay_ms(1500);

// 显示单片机电源电压

display(7456);

Delay_ms(1000);

display(system_voltage);

Delay_ms(1500);

Timer0Init(); // 初始化定时器0

while (1)

{

}

}

matlab看图像分度值,也制作开源 STC15W408AS 白光T12控制器 数码管版本 洞洞板搭建...相关推荐

  1. 【MATLAB】图像二值化(imbinarize函数)

    [MATLAB]图像二值化(imbinarize函数) 图像二值化在MATLAB 2019 版中提示:建议不要使用'im2bw'而改用'imbinarize'.所以特意总结im2bw和imbinari ...

  2. Python使用openCV把原始彩色图像转化为灰度图、使用OpenCV把图像二值化(仅仅包含黑色和白色的简化版本)、基于自适应阈值预处理(adaptive thresholding)方法

    Python使用openCV把原始彩色图像转化为灰度图.使用OpenCV把图像二值化(仅仅包含黑色和白色的简化版本).基于自适应阈值预处理(adaptive thresholding)方法 目录

  3. matlab 反向二值化,MATLAB:图像二值化、互补图(反运算)(im2bw,imcomplement函数)...

    图像二值化.反运算过程涉及到im2bw,imcomplement函数,反运算可以这么理解:原本黑的区域变为白的区域,白的区域变为黑的区域. 实现过程如下: close all;%关闭当前所有图形窗口, ...

  4. MATLAB的图像中值滤波

    下面来介绍一下图像的中值滤波方法: 1.首先打开MATLAB软件,在其主界面的编辑器中写入下列代码: I=imread('G:\MATLAB\bm.bmp');      %读取保存路径下的图片 I= ...

  5. opencv 二值化 matlab,opencv-python图像二值化函数cv2.threshold函数详解及参数cv2.THRESH...

    cv2.threshold()函数的作用是将一幅灰度图二值化,基本用法如下: #ret:暂时就认为是设定的thresh阈值,mask:二值化的图像 ret,mask = cv2.threshold(i ...

  6. MATLAB中如何让分度值小点,实验6 干涉的matlab模拟.doc

    实验6 干涉的matlab模拟.doc MATLAB在光信息处理中的应用课程上机实验(三峡大学2013年)实验6干涉的MATLAB模拟一.实验目的掌握双缝干涉.牛顿环的MATLAB模拟.二.实验内容1 ...

  7. MATLAB图像二值化

    还是老规矩先宣传一下QQ群群: 格子玻尔兹曼救星:293267908. 一切为了早日毕业. 听说可以在PPT作图然后用MATLAB二值化把复杂边界用01矩阵表达出来.上网看了下: I = imread ...

  8. matlab显示像素分布,MATLAb-----7--------如何动态显示鼠标的坐标值和图像像素值

    MATLAB如何动态显示鼠标的坐标值和图像像素值 http://apps.hi.baidu.com/share/user/41caedc2bda36d61746c6162330a (1)fix(x) ...

  9. 基于阈值的图像二值化方法MATLAB

    文章目录 一.目录 二.摘要 三.实验步骤 3.1 固定阈值法 (1)计算均值和方差 (2)绘制和分析高斯分布图像 (3)以128为阈值进行固定阈值分割 (4)观察灰度直方图选择最优固定阈值 3.2 ...

最新文章

  1. SpringCloud Alibaba 微服务架构版本说明
  2. ABAP-DOI技术的优化
  3. linux建立普通用户命令,Linux普通用户执行特定的命令配置
  4. 使用Spring使用Java发送电子邮件– GMail SMTP服务器示例
  5. php版本哪个没有面向对象,php面向对象的方法重载两种版本比较
  6. rxjs为什么用的人少_工伤为什么公司不怕打官司
  7. linux的functions之killproc函数详解
  8. 关于javascript中apply()和call()方法的区别
  9. 在slackware 10下安顿Oracle 10
  10. clamp.js 的使用---超出省略 (翻译)
  11. Web 探索之旅 | 第二部分第四课:数据库
  12. 使用npm和命令行强制删除文件
  13. 一个计算机爱好者的不完整回忆(十二)下载软件
  14. 如何制作趣味头像?分享几种制作头像的方法
  15. vCenter Server CA证书下载
  16. 倍福PLC控制器设置开机自启动
  17. 常见的12个深度学习面试问题(提高篇)
  18. WAP开发问答(1)简单的说WAP代表什么?
  19. 洛谷P1860——新魔法药水
  20. Java获取上周一周末和上月初月末

热门文章

  1. matlab已知圆心和半径怎么作圆,MATLAB中已知圆心和半径画圆
  2. Lamp 服务器环境安装
  3. java 分钟数_Java 中计算两个时间的相差分钟数
  4. 10个高质量免费学习网站
  5. 初识——雷达通信一体化技术
  6. Lambda表达式效率低
  7. ZOJ 3886 Nico Number(筛素数+Love(线)Live(段)树)
  8. 2015第16本:协同学--大自然构成的奥秘
  9. 将表格导出为excel
  10. 联想台式计算机重装系统教程,联想台式机重装系统win7图文教程