基于51单片机的恒温加热系统--main.c文件
作品完成于今年6月份,现在整理整理发上来,防止忘记。
基于51单片机的恒温加热系统-演示视频.mp4
引用的头文件
>自编的DS18B20温度传感器驱动程序源码 ds18b20.h
>自编的LCD1602液晶显示屏驱动程序源码 lcd1602.h
↓main.c文件
/*********************恒温加热系统main.c主程序****************/
/*
* 驱动的接口函数:
* DS18B20温度传感器驱动 :float DS18B20_ReadTmp(); 返回温度值
* LCD1602显示屏的驱动 : void LCD1602_WriteInstrucion(unsigned char addr);addr显示的首地址
* void LCD1602_WriteData(unsigned char dat[]);dat[]显示的数据
*/#include <reg52.h>
#include <LCD1602.h>
#include <DS18B20.h>#define EMPTY 0x20//LCD1602显示空值的宏sbit pwm = P1^6; //pwm输出IO口
bit DIR = 1; //电平标志位,为1表示接下来pwm要输出高电平,为0表示要输出低电平
bit STARTSTOP = 0; //可控硅开启还是关断状态标志位,1为开启,0为关断sbit k1 = P3^1; //按键接口
sbit k2 = P3^0;
sbit k3 = P3^2;
sbit k4 = P3^3;unsigned char a = 1;unsigned char HLevel_TH ; //定时器初始值,高电平的高8位
unsigned char HLevel_TL ; //定时器初始值,高电平的低8位
unsigned char LLevel_TH ; //定时器初始值,低电平的高8位
unsigned char LLevel_TL ; //定时器初始值,低电平的低8位unsigned int xdata PWM_CYCLE = 2000; //pwm周期,初始值设为2000 us
unsigned int Htime = 1000; //高电平时间
unsigned int Ltime = 1000; //高电平时间
unsigned int main_count; //主函数里的计数变量,用来判断是否进行测量float PV ; //保存当前温度数值的变量,present value
float SHV = 37.5; //初始的温度范围
float SLV = 37.0; unsigned char num[] = "0123456789" ; //用于温度数据转换成字符型数据的运算
unsigned char lcdbuf[16]; //用于保存lcd1602的显示数据
unsigned char xdata save[] = " save! " ;
unsigned char xdata start[] = " start! " ;
unsigned char xdata stop[] = " STOP! " ; /******函数原型声明******/
void time0_init(); //初始化定时器0
void init_calculate(unsigned int Htime,unsigned int Ltime); //计算定时器装载的初值
void pwm_out() ; //定时器中断函数
void delay(unsigned int i);
unsigned char *PVdisplay(); //处理显示当前值数组的函数指针,present value
unsigned char *SVrange(); //处理显示设置范围数组的函数指针,set value range
unsigned char *PSHV(); //请设置上限值,please set high value
unsigned char *PSLV(); //请设置下限值,please set low value
unsigned char *DUTY_RATIO(); //计算占空比,DUTY_RATIO,以xx%显示
void lcd_display(unsigned char addr,const unsigned char dat[]); //lcd显示函数
void lcd_clean(unsigned char addr,unsigned char n); //lcd清屏函数,addr为开始清屏的首地址,n为清除字符个数
bit key_do(unsigned char k); //判断按键是否按下,内嵌了消抖和等待按键松开
void SV_key(float *value, unsigned char *(*po)()); //设置数据增减的按键处理函数
void key_pros(); //处理按键的总函数
void output_regulation(unsigned int times); //负反馈调节函数/******函数原型声明结束****//*主函数*/
void main(void)
{pwm = 0; //先关闭pwm信号time0_init(); //定时器初始化LCD1602_Init(); //lcd1602显示屏初始化PV = DS18B20_ReadTmp();lcd_display(0xc0,stop);while(1){main_count++;output_regulation(20000); //经过仿真,主函数循环一次25us,则20000×25us = 0.5skey_pros();}
}/*中断处理函数*/
void pwm_out() interrupt 1
{TR0 = 0; //先关闭定时器if(DIR==0) //标志位为0,则接下来pwm输出低电平{TH0 = LLevel_TH;TL0 = LLevel_TL;pwm = 0;DIR = 1;}else //标志位为1,则接下来pwm输出低电平{TH0 = HLevel_TH;TL0 = HLevel_TL;pwm = 1;DIR = 0;}TR0 = 1; //装填完毕打开定时器
}/*
*函数名: time0_init()
*函数功能: 对定时器0进行初始化,使其一开始计时1000个数(1000 us)
*输入: 无
*输出: 无
*/
void time0_init()
{TMOD |= 0x01; //定时器工作模式为工作方式1TH0 = 0xfc;TL0 = 0x18; //定时器初始值设为定时1000 us,即pwm周期的一半ET0 = 1;EA = 1;TR0 = 0; //先不要开计时器
}/*
*函数名: init_calculate(unsigned int Htime,unsigned int Ltime)
*函数功能: 将电平时间参数转换成可以直接装载到定时器初值的格式
*输入: 高电平时间 Htime,低电平时间 Ltime。单位 us
*输出: 无
*/
void init_calculate(unsigned int Htime,unsigned int Ltime)
{HLevel_TH = (65536-Htime)/256; //将十进制的时间参数值转换成定时器的初始值格式,即高低8位HLevel_TL = (65536-Htime)%256;LLevel_TH = (65536-Ltime)/256;LLevel_TL = (65536-Ltime)%256;}/*
*函数名: delay(unsigned int i)
*函数功能: 延时函数
*输入: 延时参数 i
*输出: 无
*/
void delay(unsigned int i)
{while(i--);
}/**编写******************
*处理、运算lcd1602显示屏字符串的函数群。
*数组lcdbuf[16]装着要发送给LCD1602显示的数据,需要显示什么,
*就用函数往lcdbuf[16]数组里装什么
*//*处理显示当前值的数组,present value;在当前值后面显示pwm信号的占空比Duty Ratio xx%,精确到1% */
unsigned char *PVdisplay()
{unsigned int temp = PV*10 +0.5; //温度数据小数点向右移一位,用来运算,并使显示保留一位小数unsigned int DR = ((float)Htime/PWM_CYCLE)*100 + 0.5; //计算占空比,以xx%表示lcdbuf[0] = 'P'; lcdbuf[1] = 'V'; lcdbuf[2] = EMPTY;lcdbuf[3] = num[temp/1000]; lcdbuf[4] = num[temp%1000/100]; lcdbuf[5] = num[temp%100/10];lcdbuf[6] = '.'; lcdbuf[7] = num[temp%10]; lcdbuf[8] = 0xdf; lcdbuf[9] = 'C';lcdbuf[10] = EMPTY; lcdbuf[11] = EMPTY; lcdbuf[12] = EMPTY;lcdbuf[13] = EMPTY; lcdbuf[14] = EMPTY; lcdbuf[15] = EMPTY; /* PV xxx.x°C */if(lcdbuf[3]==('0')) //如果温度的百位是0,将该位显示空,下同{lcdbuf[3] = EMPTY; if(lcdbuf[4]==('0')) {lcdbuf[4]=EMPTY;} //温度的百位是0的情况下,如果温度的十位也是0,将该位显示空,下同}return lcdbuf; //将数组的首地址返回给函数
}/*处理显示设置范围的数组,set value range */
unsigned char *SVrange()
{unsigned int tempH = SHV*10 + 0.5; //温度数据小数点向右移一位,用来运算,并使显示保留一位小数unsigned int tempL = SLV*10 + 0.5;lcdbuf[0] = 'S'; lcdbuf[1] = 'V'; lcdbuf[2] = EMPTY; lcdbuf[3] = num[tempL/1000]; lcdbuf[4] = num[tempL%1000/100];lcdbuf[5] = num[tempL%100/10]; lcdbuf[6] = '.'; lcdbuf[7] = num[tempL%10];lcdbuf[8] = '~'; lcdbuf[9] = num[tempH/1000]; lcdbuf[10] = num[tempH%1000/100]; lcdbuf[11] = num[tempH%100/10];lcdbuf[12] = '.'; lcdbuf[13] = num[tempH%10];lcdbuf[14] = 0xdf; lcdbuf[15] = 'C'; /*SV xxx.x~xxx.x°C*/if(lcdbuf[3]==('0')) {lcdbuf[3] = EMPTY;if(lcdbuf[4]==('0')) {lcdbuf[4]=EMPTY;}}if(lcdbuf[9]==('0')) {lcdbuf[9] = EMPTY;if(lcdbuf[10]==('0')) {lcdbuf[10]=EMPTY;}}return lcdbuf;
}/*请设置上限值,please set high value */
unsigned char *PSHV()
{unsigned int temp = SHV*10 +0.5; //温度数据小数点向右移一位,用来运算,并使显示保留一位小数lcdbuf[0] = EMPTY; lcdbuf[1] = EMPTY; lcdbuf[2] = 'P'; lcdbuf[3] = 'S'; lcdbuf[4] = 'H';lcdbuf[5] = 'V'; lcdbuf[6] = EMPTY; lcdbuf[7] = num[temp/1000]; lcdbuf[8] = num[temp%1000/100]; lcdbuf[9] = num[temp%100/10]; lcdbuf[10] = '.'; lcdbuf[11] = num[temp%10];lcdbuf[12] = 0xdf; lcdbuf[13] = 'C'; lcdbuf[14] = EMPTY; lcdbuf[15] = EMPTY; /* PSHV xxx.x°C */if(lcdbuf[7]==('0')) {lcdbuf[7] = EMPTY;if(lcdbuf[8]==('0')) {lcdbuf[8]=EMPTY;}}return lcdbuf;
}/*请设置下限值,please set low value */
unsigned char *PSLV()
{unsigned int temp = SLV*10 +0.5; //温度数据小数点向右移一位,用来运算,并使显示保留一位小数lcdbuf[0] = EMPTY; lcdbuf[1] = EMPTY; lcdbuf[2] = 'P'; lcdbuf[3] = 'S'; lcdbuf[4] = 'L'; lcdbuf[5] = 'V'; lcdbuf[6] = EMPTY;lcdbuf[7] = num[temp/1000]; lcdbuf[8] = num[temp%1000/100]; lcdbuf[9] = num[temp%100/10]; lcdbuf[10] = '.'; lcdbuf[11] = num[temp%10];lcdbuf[12] = 0xdf; lcdbuf[13] = 'C'; lcdbuf[14] = EMPTY; lcdbuf[15] = EMPTY; /* PSLV xxx.x°C */if(lcdbuf[7]==('0')) {lcdbuf[7] = EMPTY;if(lcdbuf[8]==('0')) {lcdbuf[8]=EMPTY;}}return lcdbuf;
}/*计算显示占空比,DUTY_RATIO,结果以xx%显示*/
unsigned char *DUTY_RATIO()
{unsigned int DR = ((float)Htime/PWM_CYCLE)*100 + 0.5; //计算占空比,以xx%表示lcdbuf[0] = 'D'; lcdbuf[1] = 'R'; lcdbuf[2] = num[DR/10]; lcdbuf[3] = num[DR%10]; lcdbuf[4] = '%';lcdbuf[5] = EMPTY; lcdbuf[6] = EMPTY; lcdbuf[7] = EMPTY; lcdbuf[8] = EMPTY; lcdbuf[9] = EMPTY; lcdbuf[10] = EMPTY; lcdbuf[11] = EMPTY; lcdbuf[12] = EMPTY;lcdbuf[13] = EMPTY; lcdbuf[14] = EMPTY; lcdbuf[15] = EMPTY; /* PV xxx.x°C */if(lcdbuf[2]==('0')) //如果温度的百位是0,将该位显示空,下同{lcdbuf[2] = EMPTY; }return lcdbuf; //将数组的首地址返回给函数
}
/*
*函数名: lcd_display (unsigned char addr,const unsigned char dat[])
*函数功能: 控制lcd1602进行显示,默认一次发送16个字符
*输入: 需要显示的那行的首地址 addr,一般取80H或C0H;要显示的数据 dat[]。这里通过数组向函数传递要显示的数据,就可以用指针函数了
*输出: 无
*/
void lcd_display(unsigned char addr,const unsigned char dat[])
{unsigned char n;LCD1602_WriteInstrucion(addr); //写入要显示的首地址for(n=0;n<16;n++) //逐个写入显示数据{LCD1602_WriteData(dat[n]); }}
/*************************************************************
*函数名: lcd_clean(unsigned char addr,unsigned char n)
*函数功能: 清空lcd屏的指定位置
*输入: 需要清空的起始地址 addr,清空字符个数 n
*输出: 无
**************************************************************/
void lcd_clean(unsigned char addr,unsigned char n)
{unsigned char m;LCD1602_WriteInstrucion(addr); //写入要显示的首地址for(m=0;m<n;m++) //逐个写入显示数据{LCD1602_WriteData(EMPTY); }
}/*结束编写***********处理、运算lcd1602显示屏字符串的函数群*******************//*编写******************按键处理函数群*********************************//*
*函数名: key_do(unsigned char k)
*函数功能: 判断按键是否按下,按下了返回0,没按下或者消抖之后没按下返回1。这里按下返回0是为了与一般判断按键按下的情况统一,按键按下后为低电平,防止思路混乱。写这个函数是因为判断按键按下需要消抖,一次判断按下需要占用多行,且有嵌套if语句,集合成一个函数可以在应用时更简洁。
*输入: 按键选择参数k,1为判断k1,2为判断k2,3为k3,4为k4
*输出: 按下了返回0,没按下返回1。
*/
bit key_do(unsigned char k)
{switch(k){case 1:{if(k1==0){delay(1000); //按键延时消抖if(k1==0){while(!k1); //等待按键松开,若删掉这一句,长按时数据会连续增加return 0;}else return 1; }else return 1;}break;case 2:{if(k2==0){delay(1000);if(k2==0){while(!k2);return 0;}else return 1; }else return 1;}break;case 3:{if(k3==0){delay(1000);if(k3==0){while(!k3);return 0;}else return 1; }else return 1;}break;case 4:{if(k4==0){delay(1000);if(k4==0){while(!k4);return 0;}else return 1; }else return 1;}break;}return 1;}/*
*函数名: SV_key(float *value, unsigned char *(*po)())
*函数功能: 处理设置值增减的按键。按下k4增,按下k3减,按下k2选择设置位数,按下k1退出当前值的设置模式。实时显示当前的修改状态和修改的值。
*输入: 指针变量*value,可以将需要修改的设定温度值实参地址传到函数内,从而在函数内修改实参值。函数指针unsigned char *(*po)() 。向函数内传递处理lcdbuf[]数组的那一堆函数。由于向显示函数lcd_display()传递数组时,调用的是一个指针函数,而不是直接调用地址值,需要用这个指针函数对数组内的数据进行运算更新。关于函数指针的解释:首先它是一个指针,可以指向函数的指针。(*po)() 表示指向一个无参数的函数。(*po)()前的*表示所指向的函数也是一个指针,unsigned char则表示(*po)()指向的指针函数可以指向unsigned char型变量
*输出: 按下了返回0,没按下返回1。
*/
void SV_key(float *value, unsigned char *(*po)())
{unsigned char i = 1; //用来判断设置哪一位数的计数变量(小数位、个位、十位)unsigned int j; //用来闪烁判断的计数变量,闪烁当前设置的位lcd_display(0xc0,(*po)()); //液晶屏显示当前是设置哪个值(温度上限、下限)while(k1){if(key_do(2)==0) //记录k2按键按下的次数,按一次设置的位数向左移{i++;if(i==6) {i=1;} //温度数据为 xxx.x共四个位数,但是要跳过小数点,为了方便选择手动跳过小数点。//总共闪5个位置。}switch(i) //开始修改值,i为1时修改小数点后1位;为2时是小数点,需要手动跳过{case 1: //改变小数点后1位{if(key_do(4)==0) {*value += 0.1;} //k4为数据增加if(key_do(3)==0) {*value -= 0.1;} //k3为数据减少}break;case 2: break; //i为2时闪烁小数点,跳过case 3: //改变个位{if(key_do(4)==0) {*value += 1;} if(key_do(3)==0) {*value -= 1;} }break;case 4: //改变十位{if(key_do(4)==0) {*value += 10;} if(key_do(3)==0) {*value -= 10;} }break;case 5: //改变百位{if(key_do(4)==0) {*value += 100;} if(key_do(3)==0) {*value -= 100;} }break;}j++;if(j==3000) {lcd_display(0xc0,(*po)());} //发送完整的数据。此处的j值需待调试*if(j==6000) {lcd_clean(0xcc-i,1); j = 0;} //发送缺失当前修改位的数据,达到闪烁的效果//地址值0xcc-i看word文档的显示内容与地址对照表if(key_do(1)==0){break;} //当按下k1,退出当前设置模式,break可以跳出一层while循环}
}/*
*函数名: key_pros()
*函数功能: 处理按键的总函数,按键功能为:按下k1进入启停可控硅,长按k2进入设置温度范围模式
*输入: 无
*输出: 无
*/
void key_pros()
{/* 按键修改恒温范围 */if(k2==0){unsigned char i; //用来计数while(!k2){delay(1000); //延时9毫秒i++;if(i>200){lcd_display(0xc0,PSLV());break;} //调用显示函数提醒用户可以进入设置模式了}while(!k2); //上一个while退出可能是break控制,而不是循环条件控制,需要确保k2松开,防止长按出错if(i>200) //确认长按了k2,进入设置温度下限模式{lcd_display(0xc0,PSLV()); //显示PSLV字样,please set low value,设置温度下限for(i=0;i<200;i++){delay(1000);}SV_key(&SLV,PSLV); //处理下限值的增减SV_key(&SHV,PSHV); //处理上限值的增减lcd_display(0xc0,save); //显示屏显示 save! 字样 ,表示设置完毕for(i=0;i<200;i++){delay(1000);} //延时约两秒,用来显示save字样lcd_display(0xc0,SVrange()); //显示温度范围}}
/* 按键控制加热模块启停 */if(key_do(1)==0){STARTSTOP = ~STARTSTOP;if(STARTSTOP==1){unsigned char i;lcd_display(0xc0,start);for(i=0;i<200;i++){delay(1000);} //延时约两秒,用来显示save字样lcd_display(0xc0,SVrange());TR0 = 1; //开启定时器}if(STARTSTOP==0){TR0 = 0; //关闭定时器pwm = 0; //关断可控硅lcd_display(0xc0,stop);}}}/*结束编写**************************按键处理函数群*******************************//*
*函数名: output_regulation(unsigned int time)
*函数功能: 在主函数内进行计数,计数次数到达预设值时(间隔一定时间)进行一次温度测量。并在可控硅开启的状态下给出负反馈调节。
*输入: 测量温度的时间间隔times
*输出: 无
*/
void output_regulation(unsigned int time)
{static unsigned char i;i++;if(main_count>time) //计数值到达设定的次数(时间)后,进行一次温度测量和反馈调节{ main_count = 0;TR0 = 0; //关一下定时器,防止定时器频繁的中断干扰传感器的通讯PV = DS18B20_ReadTmp(); //测量温度值if(STARTSTOP==1){TR0 = 1;} //读取温度完毕,开定时器lcd_display(0x80,PVdisplay()); //更新显示的测量值if(STARTSTOP==1){lcd_display(0x8b,DUTY_RATIO());} //在温度数据后面显示占空比if(STARTSTOP==1&&(i>=50)) //可控硅开启状态下,进行pwm占空比的调节{i = 0;if((PV>SHV)&&(Htime>20)) //当测量值大于预设上限,且高电平时间不小于一个调节不变化量时(防止高电平时间超过pwm的周期){ Htime = Htime - 20;Ltime = PWM_CYCLE - Htime;}if((PV<SLV)&&(Ltime>20)) //保证pwm低电平时间不小于等于0{Htime = Htime + 20;Ltime = PWM_CYCLE - Htime;}init_calculate(Htime,Ltime); //进行一次计时器初值的运算lcd_display(0x8b,DUTY_RATIO()); //在温度数据后面显示占空比}}
}
基于51单片机的恒温加热系统--main.c文件相关推荐
- 恒温控制系统单片机仿真c语言,基于51单片机的恒温控制系统
#include void main()//主函数 { init();//初始化函数 while(1) { temp_control();//控制按键函数 unnormal_proccessing(g ...
- 基于51单片机无线恒温箱恒温控制系统(原理图+源代码+PCB+参考论文)
本设计: 基于51单片机无线恒温箱恒温控制系统(全套资料+原理图+源代码+PCB+参考论文) 原理图:Altium Designer 程序编译器:keil 4 编程语言:C语言 编号C0020 下载链 ...
- 基于51单片机的DS18B20温度控制加热降温系统设计
基于51单片机的DS18B20温度控制加热降温系统设计 1 开发环境 2 功能说明介绍 3 仿真图 4 程序 5 原理图 6 视频讲解 7 设计报告 7.1 设计目的 7.2 概述 7.3 国内外研究 ...
- 基于51单片机的智能洗衣机
提示:记录2022年4月做的毕设 文章目录 前言 一.任务书 1.1设计(研究)目标: 1.2设计(研究)内容: 二.设计思路 三.硬件 四.代码以及框图等资料 喜欢请点赞哦! 前言 基于51单片机的 ...
- 基于单片机的超市储物柜设计_基于51单片机对电子储物柜系统的设计
电路描述: 安全是我们日常的生活中最关心的问题. 每个人都觉得安全问题是非常至关重要的,在家里的门和安全,可以尽可能多的安全. 为了对于门访问安全 因此,我们打算通过引进一个电子密码锁系统,该系统包括 ...
- 简易电子钟c语言程序,(最新整理)基于51单片机的电子钟C语言程序
<(最新整理)基于51单片机的电子钟C语言程序>由会员分享,可在线阅读,更多相关<(最新整理)基于51单片机的电子钟C语言程序(9页珍藏版)>请在人人文库网上搜索. 1.完整) ...
- 基于51单片机电子时钟
keil工程与protues仿真电路 1 基于51单片机用LCD1602实现时-分的显示 2. 按键控制时-分的调整 3. 能实现整时报时的功能(蜂鸣器响) 4. 闹钟模式 5.按键切换模式(模式一: ...
- 基于51单片机的密码锁
本次的项目是基于51单片机的密码锁,适合用来作为课程设计 主要实现了:密码锁功能,通过矩阵按键输入密码,输入范围为0-F,共有四位:同时,也可以使用更改按键更改密码,在更改时需要输入原密码来进入更改模 ...
- 单片机六位抢答器c语言程序,八路电子抢答器(基于51单片机的8路抢答器设计C语言程序)...
哥,你还有AT89C51单片机8路抢答器的资料吗 哥,你还有AT89C51单片机8路抢答器的资料吗 AT89C51单片机8路抢答器的资料 源程序如下 #include #define uchar un ...
- 基于51单片机数码管显示
基于51单片机数码管显示 ## 数码管显示原理 数码管(LED Segment Displays)由多个发光二极管封装在一起组成"8"字型的器件,引线已在内部连接完成,只需引出它们 ...
最新文章
- Discovery studio画蛋白质构象叠合图
- python web框架autoreload原理(以bottle为例)
- Apache Lucene与Lucene.Net——全文检索服务器
- oracle设置开机启动,linux下oracle设置开机自启动实现方法
- Embeded linux之移植iptables
- 阿里P7架构师要求:Web核心+开源框架+大型网站架构!含面试题目!
- Java虚拟机(二)——垃圾回收与内存分配
- Google Code Review 如何进行代码审查
- 模糊控制算法在MATLAB/SIMULINK中的应用
- 微信小程序 editor富文本编辑器组件封装
- Java项目部署到远程服务器(详细步骤)
- oracle数据文件头损坏6,恢复数据库时遇到数据文件头损坏 | 信春哥,系统稳,闭眼上线不回滚!...
- 什么是a站、b站、c站、d站、e站、f站、g站、h站、i站、j站、k站、l站、m站、n站…z站?
- 企鹅号怎么赚钱,企鹅号怎么运营,企鹅号怎么写爆文
- 机器学习——验证方法
- PHP 抓取接口和网页(爬取方式)
- Linux signal、sigaction的使用总结
- 语音识别原理与应用学习笔记
- 【历史上的今天】9 月 11 日:Adobe 公司联合创始人出生;现代游戏机鼻祖诞生;谷歌推出 Android Pay
- 电商用户数据分析报告
热门文章
- Mybatis的代码
- 2018年江西省电子现场赛赛题
- 云计算数据中心运维管理的五大重点
- 2013-2014 ACM-ICPC, NEERC, Southern Subregional Contest Problem F. Judging Time Prediction 优先队列...
- 【PyTorch】CUDA error: device-side assert triggered
- 码无止境(1)——一个科研项目立项时的小程序(字典嵌套列表)
- 开咖啡店如何盈利?用50家咖啡店揭晓一套盈利模式
- 在salesforce中实现鼠标悬停显示提示框效果,并对显示框内容进行微缩页面布局
- Linux系统下安装matla版libsvm
- 浅谈领导力理解和体会