包含,上位机、下位机
程序界面:
电路原理图如下:
智能家居是通过简单的操作实现对家居的管理以提升家居便利性、舒适性的一种方式。本设计的主要功能是使用户能够在一定距离内以无线的方式控制设备的开关、工作模式、工作功率等以及监测实时室温和室温变化。
内容和要求:
通过不同的按钮控制设备工作在不同的模式
通过在用户界面上的简单操作实现对设备工作功率的调节
实现设备的定时开关
上位机显示实时室温以及温度变化曲线
本设计的硬件电路较简单,主要是上位机与下位机的程序设计,难点在于上位机与下位机的通信协议设置以及下位机对硬件设备的控制。
1.1系统功能及框图 本设计功能主要是对风扇、LED两个设备的控制以及通过温度传感器采集温度实现实时温度监测。上位机和下位机通过wifi模块实现一定距离内的无线通信。上位机向下位机发送命令,下位机接受命令并直接控制各设备,然后向上位机返回一定的信息(下图箭头方向表示数据流动方向)。 图1-1 总体框图 1.1.1上位机 使用PC作为上位机,上位机控制软件由基于WinForm的C#编写。 首先,相比较VB.Net而言,C#支持无符号整数,这对于需要频繁与下位机交换正整数信息的上位机软件而言是必须的;其次,VB 对大小写不敏感,而C#是大小写敏感的,对于习惯使用C语言编程的人而言C#更合适;最后,C#书写的代码比VB平均短小20%。 综合考虑,本设计采用的编程语言是C#。
1.1.2单片机系统 单片机使用的是STM32F103RCT6。 首先,STM32使用3.3V电压供电,本设计采用的wifi模块也使用3.3V供电,相比较5V供电的51单片机而言,省去了电平匹配电路;其次,本设计需要输出不同占空比的PWM波以控制设备的功率,STM32有2个16位的6通道高级控制定时器,最多6个通道可用于PWM输出,符合设计要求。 1.1.3 LED 该绿色LED用来模拟可由单片机直接控制的小功率设备,有三种工作模式。 模式一:以1s为周期进行亮灭 模式二:以2s为周期进行亮灭 模式三:常亮
1.1.4风扇 风扇工作需要较大的电流,无法使用单片机直接驱动,因此在单片机与风扇之间加入驱动电路以提高单片机的驱动能力。该风扇用来模拟无法直接用单片机驱动的大功率设备。 该风扇可以根据上位机的设置按一定的功率进行工作。LED(红)用来指示风扇的功率。
1.1.5温度采集 传感器使用的是温度传感器DS18B20。 DS18B20是美国Dallas半导体公司的新一代数字式温度传感器,它具有独特的单总线接口方式,即允许在一条信号线上挂接数十甚至上百个数字式传感器,从而使测温装置与各传感器的接口变得十分简单,克服了模拟式传感器与微机接口时需要的A/D转换器及其他复杂外围电路的缺点,而且,可以通过总线供电,由它组成的温度测控系统非常方便,而且成本低、体积小、可靠性高。DS18B20的测温范围-55~+125℃最高分辨率可达0.0625℃,完全符合本设计的要求。
1.1.6 wifi模块 本设计使用了两个wifi模块。一个与电脑通过串口相连,一个与下位机通过串口相连。两个wifi模块直接通过无线传输串口送来的数据。数据流通如下所示: 图1-2 数据流通
1.2 通信协议方案 上位机向下位机发送的控制信息大部分是0—100的数字,要区分这些数字是作用于哪个设备的控制信息就需要设计上位机与下位机的通信协议。 方案一:上位机在发送数字指令前先将数字转换成字符串,在该字符串前加上标志位,下位机接收到该字符串后去除标志位,并将剩余字符串转化为数字。 方案二:上位机在发送数字指令前先发送字符指令,通过不同的字符指令使得下位机中对应的标志位置位,其余标志位复位,再发送数字指令。下位机根据各标志位的状态分辨出该数字指令是作用于哪个设备。 方案一不需要开辟内存空间设置标志位,代码量较少,上位机编程简单,但下位机对字符串的处理难度较大。方案二代码量大,需要的内存空间较大,但编程思路简单明了,易于实现。综合考虑,本设计使用方案二。
1.3实时温度采集方案 上位机实时温度曲线的绘制需要实时采集温度以更新用于绘制曲线的数组。 方案一:使用下位机的定时器,以1s为周期向上位机发送实时温度值。 方案二:使用上位机的timer控件,以1s为周期想下位机发送请求,利用下位机的串口中断获取实时温度值。 下位机定时器的初始化配置和开启与关闭较为复杂,且新增一个定时器中断也会影响下位机的工作性能。相比较而言,上位机的timer控件使用较简单,而且获取实时温度使用的是下位机的串口中断,不必新增中断源。综合考虑,本设计使用方案二。
硬件电路由STM32最小系统、DS18B20温度采集、LED驱动、风扇驱动这四个部分组成。下图为硬件原理图。
图2-1 硬件原理图
2.1 STM32最小系统 最小系统是一个可以正常启动和运行的单片系统,该系统中任何部件都是完全必要的,缺一不可的。在微处理器其他所有的设计中,都包含这个最小系统。最小系统的复杂程度,表明了该处理器的单片化程度。
图2-2 最小系统 图2-2是一个最小系统图,图中包括电源、时钟和复位电路。 - 电源:STM32F1xx系列采用低电源工作,电源工作范围是2.0~3.6V,常规设计一般选用3.3V电源。
- 复位控制:复位控制虽然十分重要,但设计一般都很简单。本设计采用按键复位。
- 时钟:时钟是微处理器最重要的部分,时钟的来源是振荡器。在STM32F1xx系列产品中,其芯片内嵌一个8MHz的RC振荡器,在芯片上电复位以后,这个RC振荡器首先起振,为系统提供时钟源。但是芯片内部的RC振荡器精度不高,在需要高精度时钟的场合还必须使用外部振荡器。本设计中需要实现异步通信,因此需要外接振荡器以提供高精度时钟。
STM32F1xx系列有两个外部振荡器接口。 系统主时钟是系统除看门狗以外所有功能的时钟。这个时钟既可以选内部RC振荡器作为时钟源,也可以外部接一个时钟源。本设计中使用外部8MHz的时钟源而不使用内部RC振荡器以提高精度。 B.系统辅助时钟外部振荡器接口 辅助时钟作为看门狗的时钟源,也可以选作实时时钟RTC的时钟源。驱动辅助时钟的振荡器也有两个,一个是内部32kHz的RC振荡器,另一个是外部32.768kHz的振荡器接口。本设计中外接一个32.768kHz的晶体振荡器以提供精确辅助时钟。 2.2 DS18B20温度采集 DS18B20的信号线接在PA0口,接收单片机的指令并向单片机返回所测温度值。 图2-3 温度采集 上图表明DS18B20与单片机的连接。
2.3 LED驱动 LED1即上述的具有三种工作模式的LED,LED0与风扇共同受一个IO口控制,用以指示风扇的工作功率。 图2-4 LED驱动 LED采用共阳连接,LED正极与上拉电阻相连,负极与IO口相连。
2.4风扇驱动 风扇用在原理图中用DS3表示。 虽然风扇与LED0共同受一个IO口控制,但风扇功率较大,无法使用IO口直接驱动,需要增加驱动电路。 控制模块工作原理:当单片机控制端加低电平时,三极管Q2导通,这时Q2的上端,也就是C极被系统拉到0V左右,这时MOS管Q1的G极为0V,所以此时MOS管的DS端不会导通。当单片机控制端加高电平时,三极管Q2不导通,这时Q2的上端,也就是C极电位为Vcc-R9两端的压降,电流穿过R8后被稳压管D3稳压在5V,则此时MOS管的G极电压为5V,所以MOS管导通,负载工作。
图2-5 风扇驱动
软件设计分为上位机程序和下位机程序两个部分。上位机程序用C#编写,采用事件触发的方式便于用户操作,主要功能是向下位机发送各种命令,并接受下位机的返回信息以一定方式显示出来。下位机程序用C语言编写,主要包括各设备的驱动程序以及与上位机通信的串口中断程序。
3.1上位机程序 上位机界面控制部分由上位机控制、LED控制、风扇控制、温度监测和温度曲线这五个部分组成。显示部分包括当前时间与定时时间的显示、信息返回(下位机返回的信息)、曲线绘制三个部分。 图3-1 上位机整体框图
3.1.1上位机控制 上位机控制部分总共有三个按钮:打开端口、关闭端口和退出。 考虑到该上位机专用于本次设计的下位机,也是为了简化设计,端口号、波特率、数据位等属性都在窗口载入时固定下来,无法在程序运行时进行修改。 图3-2 上位机控制部分 “打开端口”即打开默认端口。为防止打开端口时因为端口号或其他原因出现的问题使得程序抛出异常,在“打开端口”按钮的触发事件中加入了try-catch语句。 “关闭端口”即关闭当前打开的端口。当端口未打开时该按钮设置为不可用,只有当有端口打开才可用。“退出”即关闭当前窗体。
3.1.2 LED控制 控制面板上的三个模式选择按钮对应与上述的LED的三个工作模式。“关闭”按钮即关闭LED。(模式一:以1s为周期进行亮灭;模式二:以2s为周期进行亮灭;模式三:常亮) 图3-3 LED控制部分 该部分的标志字符是’a’。该部分的四个按钮在按下后都会先发送’a’使下位机对相应的标志位进行置位复位操作。然后再发送数字命令使下位机进入不同的模式。最后弹出对话框,解释对应模式的具体功能。
3.1.3风扇控制 该部分由风扇控制和时间显示两部分组成。控制部分具有控制风扇开关、工作功率以及定时功能。时间显示部分主要是服务于定时功能。该部分显示当前时间和定时时间,当两者相同时执行定时设置的操作。若未使用定时功能,则不显示定时时间。 图3-4风扇控制部分 图3-5时间显示部分 为节省窗体面板空间以及便于用户使用,该部分进行了控件的层叠。 具体如下: (1)定时时间选择框在初始加载时不可见,字符“时分秒”不可见,“定时开”“定时关”按钮不可见,“设置完成”按钮不可见。 (2)单击“定时设置”,“开”“关”按钮不可见,定时时间选择框可见,字符“时分秒”可见,“定时设置”按钮不可见,“定时开”“定时关”按钮可见,并弹出对话框“请选择定时开或定时关”。 a.单击“定时开”,“定时开”“定时关”按钮不可见,“设置完成”按钮可见,弹出对话框“请设置开启时间和功率”。 b.单击“定时关”,“定时开”“定时关”按钮不可见,“设置完成”按钮可见,弹出对话框“请设置关闭时间”。 (3)单击“设置完成”,“设置时间”可见,“开关控制”部分恢复原样。 时分秒以及功率调节的下拉框选项都是在窗体载入时初始化的。 若不使用定时功能,则点击“开”,设备按最大功率运行,点击“关”,设备关闭,点击“设置功率”,设备按设定的功率运行。该部分发送的标志字符是’c’。 若使用定时功能,具体操作如上所述。设置完成后设置的时间会在“定时时间”后的TIME处显示,并与当前时间比较(当前时间为系统时间,由timer1每秒刷新一次),当两者相等时timer控件会向下位机发送相应的命令。
3.1.4温度监测 该部分分为温度检测和温度曲线两个部分。第一部分可以设置温度上下限以及查询当前温度。当温度超过上限或下限时将发出相应的警报。第二部分可以生成实时温度曲线,用于直观地显示出温度变化趋势。 图3-6 温度检测 下拉框初始化与风扇控制部分相同。 “温限设置”按钮用于向下位机发送温限修改指令,即将下拉框中的温度上限和温度下限设置为下位机温度的界限。 “温度显示”按钮用于向下位机发送实时温度查询请求。下位机返回的实时温度信息将显示在“信息返回”部分的文本框中。 “温限设置”的标志字符为’b’,“温度显示”的标志字符为’m’。 图3-7 绘制温度曲线 “绘制曲线”用于绘制曲线,按下后启动timer2,每秒向下位机发送一次温度采集请求,同时将数据读取方式标志位置位(为方便下位机返回的信息显示在文本框中,上位机默认使用字符方式读取串口数据,当需要绘制曲线时,要更改为使用字节方式读取串口数据,读取方式由数据读取方式标志位决定)。 从串口读取到的数据经过相应算法处理后生成温度曲线图像显示在图片框中。该图片框也由timer2控制,每秒刷新一次。 timer2温度采集请求的标志字符为’t’。 “停止绘制”用于撤销“绘制曲线”按钮的操作。此时图片框中显示最后一次刷新出的图片并保持。当再次启动曲线绘制时图片框清空重新绘制。
3.2下位机程序 下位机程序主要由以下几个部分构成:LED驱动程序(因为风扇与指示其功率大小的LED由同一个IO口控制,所以没有专门的风扇驱动程序)、DS18B20驱动程序、PWM波生成程序、定时器设置程序、串口中断服务程序。
3.2.1主函数 主函数完成各部分的初始化,完成风扇功率的设置以及控制LED工作在不同的模式下,其余功能由各中断函数完成。
与LED相连的IO口设置为推挽输出。LED的三种显示模式均在主函数中完成,由标志位LED_model控制。该标志位由串口中断服务程序根据上位机发送过来的数据进行置位或复位。
3.2.3串口通信
串口波特率为9600,数据位8位,1位停止位,无奇偶校验位,无硬件数据流控制,采用收发模式。
串口中断服务程序主要包括两个部分:(1)根据上位机发送的信息对标志位进行置位或复位;(2)根据设置的标志位执行相应的操作。
串口中断服务程序调用相当频繁。
该程序读取上位机发送过来的标志信息,对各标志位进行置位或复位操作,再根据上位机发送过来的数字信息修改相应的变量。
该程序根据上位机发送过来的命令给出响应信息,根据上位机的要求返回对应的信息(例如温度值)。
以下为各标志位以及部分变量的解释:
temp_trx:下位机从缓冲区读取到的上位机数据。
LED_model:LED模式,不同取值会使LED进入不同状态。取值1到3表示三种工作模式,取值4表示熄灭。
fan_rate:风扇功率。例如该变量取值50,则风扇以50%的功率工作。
temp_show:温度显示标志,该标志位置位表示下位机接收到上位机采集单个温度值的请求。
LED_flag:LED控制信息标识,当下位机接收到LED控制字符时该标志位置位,表示上位机接下来发送的数据是用于对与LED相关的变量操作。
temp_flag:温度控制信息标识,当下位机接收到温度上下限控制字符时该标志位置位,表示上位机接下来发送的数据是用于对温度上下限的修改。
fan_flag:风扇控制信息标识,当下位机接收到风扇控制字符时该标志位置位,表示上位机接下来发送的数据是用于对风扇功率的修改。
fan_send_flag:用于屏蔽标志字符,该位用于识别数据是控制字符还是实际控制信息。
temp_send_flag:用于分辨上下限或标志字符,当该位为0时表示目前数据为标志位,不予处理;当该位为1时表示目前数据是用于温度上限修改;当该位为2是表示目前数据是用于温度下限修改。
temp_draw:实时温度连续发送的标志,当上位机timer2控件工作时每次发送控制字符改为置位,并返回实时温度值,最后复位。
3.2.4定时器设置
定时器以1s的周期采集温度并更新存储的温度值,同时检测当前温度是否超出温度上限或温度下限,如果超出,向上位机发出警告。
本次设计是我第一次将C#和ARM架构的单片机用于实际项目中。在整个设计过程中也遇到了许多问题。以下是对于一些问题的总结:
(1)问题:定时器中断的初始化放在PWM输出的初始化之前则PWM波无法正常输出
解决办法:调换语句位置
问题:当指示风扇功率的LED功率最大时风扇功率最小
解决办法:在控制信号接入风扇控制端前加上反向器。由于LED为共阳连接,当单片机控制端输出低电平时LED工作,然而此时控制电路的三极管导通,MOS栅极电压较低,无法导通,风扇不工作。
(3)问题:上位机定时设置返回后无法读取设定值
解决办法:根据存储在字符串中的时间信息进行处理
(4)问题:上位机定时时间显示不正常,年月日变化长度变化
解决办法:先获取系统时间日期的长度,再根据这个长度来截取字符串
(5)问题:上位机实时温度数组无法正常刷新
解决办法:每次接收到温度值并刷新数组后将缓冲区清空
(6)问题:上位机定时功能有时无法实现
解决办法:更改定时时间选择下拉框初始化程序。为了美观,当时间选择为个位数时默认在前面加上“0”,但由于系统时间的小时位为个位时不会自动补“0”,因此当系统时间的小时为0~9时无法正常完成比较,因此定时功能无法实现。
问题:WIFI模块无法正常完成数据透传
解决办法:设为AP模式的模块与下位机相连,设为STA模式的模块与上位机相连。
问题:PWM波输出IO口最低电平2.2V风扇无法工作
解决办法:将上位机送来的功率值乘三作为PWM波占空比。PWM输出在初始设置时占空比值为0~300,而上位机送来的功率值只是0~100,乘三后才能对应。
在之前的设计中,我一直使用现成的通用上位机软件,只能通过文本框将简单的数字或字符命令发送给下位机并显示下位机的返回信息。在自己动手编写上位机软件之后,我对这一工具有了新的认识,思维也得到了开阔。上位机软件不仅可以发送命令,接受显示信息,还能辅助下位机完成一定的工作。例如在本设计中,对于实时温度的采集,我采用的是上位机软件中的timer控件,而不是下位机的定时器。这样既可以减轻下位机的负担,也省去了下位机定时器复杂配置的步骤。
相比较之前一直使用51单片机而言,这次使用STM32也是一个不小的变化。虽然STM32的使用比51单片机复杂许多,但它的功能确实比51单片机强大许多。例如定时器,51单片机只有三个定时器,如果需要使用一个定时器输出PWM波,那么剩下的两个定时器对于功能较为复杂的系统而言就显得捉襟见肘。而STM32的8个定时器完全足够满足一般的系统设计,而且STM32能够通过较简单的设置完成PWM波的输出。
本次设计的功能仍有不足之处。例如温度曲线的显示,目前只做到了实时温度的采集以及简单显示,没有绘制出坐标轴,也不能根据数据的多少来调整坐标。而且控制面板的布局也不尽合理,实时温度曲线的显示部分并不是与其他功能同时使用,因此可以设计弹窗显示以精简控制面板。不过就实时温度采集以及用于存储实时温度值的数组更新我已经花费了大量的时间调试,在此过程中我对于VS2012的调试功能有了进一步的掌握。
本次设计实现的控制功能比较简单,但我从中学到了很多,尤其是C#程序设计。今后我会继续使用这一工具,将上位机与下位机紧密结合,设计出更高效的系统。
C#源程序如下:
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Windows.Forms;
- using System.IO.Ports;
- using CCWin;
- namespace project4
- {
- public partial class Form1 : Skin_Mac
- {
- public Form1()
- {
- InitializeComponent();
- System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
- //label2.Text = DateTime.Now.ToString();
- timer1.Enabled = true;
- }
- private void button1_Click(object sender, EventArgs e)
- {
- try
- {
- serialPort1.Open();//打开串口
- button1.Enabled = false;//打开串口按钮不可用
- button2.Enabled = true;//关闭串口按钮可用
- }
- catch
- {
- MessageBox.Show("端口错误,请检查串口", "错误");
- }
- }
- private void Form1_Load(object sender, EventArgs e)
- {
- int i;
- //初始化功率设置下拉框
- for (i = 1; i < 100; i++)//百分比范围(1-99)
- {
- comboBox1.Items.Add(i.ToString() + " ﹪");
- }
- comboBox1.Text = "1 ﹪";//设置默认值
- //初始化温度上限设置下拉框
- for (i = 0; i < 100; i++)//温度上限范围(0-99)
- {
- comboBox2.Items.Add(i.ToString() + " ℃");
- }
- comboBox2.Text = "30 ℃";
- //初始化温度下限设置下拉框
- for (i = 0; i <= 99; i++)//温度下限范围(0-99)
- {
- comboBox3.Items.Add(i.ToString() + " ℃");
- }
- comboBox3.Text = "0 ℃";
- //初始化定时时间设置下拉框
- // (特别说明)当数字不超过10的时候默认只有一位,为了与系统时间格式相同,在0~9前加上字符串"0"
- //此处不能加"0",若加上"0"则与系统时间不匹配,无法顺利完成比较
-
- //设置时间与系统时间保持一致既是为了美观,也是为了便于比较
- for (i = 0; i < 10; i++)
- {
- comboBox4.Items.Add(i.ToString());
- }
- for (i = 10; i < 24; i++)
- {
- comboBox4.Items.Add(i.ToString());
- }
- comboBox4.Text = "00";
- for (i = 0; i < 10; i++)
- {
- comboBox5.Items.Add("0" + i.ToString());
- }
- for (i =10; i < 60; i++)
- {
- comboBox5.Items.Add(i.ToString());
- }
- comboBox5.Text = "00";
- for (i = 0; i < 10; i++)
- {
- comboBox6.Items.Add("0" + i.ToString());
- }
- for (i =10; i < 60; i++)
- {
- comboBox6.Items.Add(i.ToString());
- }
- comboBox6.Text = "00";
- serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);//添加事件处理程序
- }
- //当开始绘制温度曲线时,将该标志位置位,刷新温度数组
- bool temp_show_flag = false;
- //温度数组
- int[]temp_data=new int[100];
- //下位机将温度分为三部分发送过来,该标志位用来识别温度值的各部分
- int temp_data_flag = 0;
- //存储温度值各部分的数组
- int[] data = { 0,0,0};
- //当串口接受到下位机发过来的数据时将其显示在文本框中
- private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
- {
- if (temp_show_flag == false)
- {
- string str = serialPort1.ReadExisting();//字符串方式读
- textBox1.AppendText(str);//添加内容textBox文本框中依次向后显示
- }
- else
- {
- //int len = this.serialPort1.ReadBufferSize;
- //string str = serialPort1.ReadExisting();//字符串方式读
- //textBox1.AppendText(str+" "+'a');
- //string s0 = str.Substring(0, 1);
- //string s1 = str.Substring(1, 1);
- //string s2 = str.Substring(2, 1);
- //byte[] buf = new byte[3];
- //buf[0] = Convert.ToByte(s0, 16);
- //buf[1] = Convert.ToByte(s1, 16);
- //buf[2] = Convert.ToByte(s2, 16);
- //this.serialPort1.Read(buf, 0, 3);
- //data[0] = Convert.ToInt16(buf[0]);
- //data[1] = Convert.ToInt16(buf[1]);
- //data[2] = Convert.ToInt16(buf[2]);
- //data[0] = (data[0] - 48) * 100 + (data[1] - 48) * 10 + (data[2] - 48);
- //int i;
- //for (i = 0; i < 99; i++)
- //{
- // temp_data[i] = temp_data[i + 1];
- //}
- //temp_data[99] = data[0];
- //textBox1.AppendText(buf[0].ToString());
- //textBox1.AppendText("\n");
- //textBox1.AppendText(buf[1].ToString());
- //textBox1.AppendText("\n");
- //textBox1.AppendText(buf[2].ToString());
- //textBox1.AppendText("\n");
- if (temp_data_flag == 0)
- {
- data[0] = serialPort1.ReadByte();
- temp_data_flag++;
- }
- else if (temp_data_flag == 1)
- {
- data[1] = serialPort1.ReadByte();
- temp_data_flag++;
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
单片机源程序如下:
- #include "led.h"
- #include "delay.h"
- #include "sys.h"
- #include "usart.h"
- #include "timer.h"
- #include "pwm.h"
- #include "DS18B20.h"
- int main(void)
- {
-
- delay_init(); //延时函数初始化
- NVIC_Configuration();// 设置中断优先级分组 确定每个中断的优先级在uart_init中实现
- uart_init(9600); //串口初始化为9600
- LED_Init(); //初始化与LED连接的硬件接口
- TIM1_PWM_Init(299,0);//不分频。PWM频率=72000/(299+1)=240Khz
- TIM3_Int_Init(9999,7199);//10Khz的计数频率,计数到10000为1s
-
- // LED0=0;
- while(1)
- {
- TIM_SetCompare1(TIM1,fan_rate);
-
- if(LED_model==1)
- {
- LED1=0;
- delay_ms(500);
- LED1=1;
- delay_ms(500);
- LED1=0;
- delay_ms(500);
- LED1=1;
- delay_ms(500);
- }
- else if(LED_model==2)
- {
- LED1=0;
- delay_ms(1000);
- LED1=1;
- delay_ms(1000);
- // printf("mark\r\n");
- }
- else if(LED_model==3)
- {
- LED1=0;
- }
- else if(LED_model==4)
- {
- LED1=1;
- }
- }
- }
复制代码
代码下载:
C#写的温度.7z
(3.44 MB, 下载次数: 137)
|