历史上的今天
今天是:2024年11月25日(星期一)
2020年11月25日 | STC15W408AS单片机白光T12控制器
2020-11-25 来源:51hei
自己设计和制作的stc白光,附上原理图,洞洞板走线图和源代码。
只实现了简单的温控,短按一下编码器快速设定温度为300℃,双击一下编码器快速设定为10℃(相当于短暂休眠



代码注释还是很详细的,适合初学者学习。
单片机源程序如下:
/**
布满星星的天空 CZS 编写
*/
#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) { //停止信号
史海拾趣
|
近期,遇到IC创业的三大怪 第一怪、海归当做添头卖 有好几个创业团队,均本土的,找到我,要我帮他们猎几个留学生,只要是有海外学历并工作过几年的,目的是写进核心团队里,装装门面,以便申报项目时能够增加评分的分数,可以申报多点资金。要求 ...… 查看全部问答> |
|
在wm5.0中如何连接网络打印机, 网络打印机是在公网上的一台共享打印机, 需要在wm5.0中连接上这台共享打印机, 并打印wm5.0中的内容. 哪位高手能够提供一点思路… 查看全部问答> |
|
急急急急急急急急急急急!!!!!!最近在接触英特尔手册中的APIC,谁能告诉我触发模式是什么???高手请进 Trigger mode 触发模式。分为edge,level两种触发方式 电子课几乎没听过,我在学APIC时碰到的,谁能给我简单的说说触发模式是什么,通俗点最好,结合APIC说说,万分感谢了!!!!!!!!… 查看全部问答> |
|
请教大家,将wince6.0升级到r3之后,explorer无法启动,进者有分 我将wince6.0升级到r3之后,重新编译原来在r2下通过的工程,编译成功后down到开发板上运行。 发现桌面无法加载,看打印消息,发现没有打印如下信息: explorer(v2.0) taskbar thread started 而taskbar的進程就是explorer.exe。 而且我发现ceco ...… 查看全部问答> |
|
GlobalLogic 急聘Mobile Development Lead Job Responsibilities: l Lead development team to ensure development projects are delivered to clients in a timely manner and meet the company\'s standard of quality and requiremen ...… 查看全部问答> |
|
首先声明,以下只是我的个人想法,具体可行性和实现,还需要经过很过拷问 开始了 在做单片机开发的时候,特别是中小型的系统中,常常是 输入-》处理-》输出 即是根据不同的输入来处理,然后生成不输出 那么是不是可以这样来理解 将 ...… 查看全部问答> |
|
我在做一个有关SD卡的项目,项目对SD卡读写速度要求较高。我的芯片是STM32F103ZET6,现在用了一块SDHC的卡,卡的时钟现在用24MHz。4线读写。理论上可以有12MB/S的速度,现在用了STM32提供的V4.2.0版本的库,DMA模式。发现SD_ReadBlock实际读取 ...… 查看全部问答> |




