随着经济不断发展以及人们生活节奏的不断加快,人们越来越重视身心健康。目前我国的“亚健康”人群的比重已达到70%左右,超过了9亿人,而用推拿按摩进行保健是我国中华医学的传统项目,因为老幼皆宜,应用范围十分广泛。随着具有保健功能的各种按摩器等产品出现。也因为在舒缓压力,缓解疲劳等方面的效果显著而备受处于“亚健康”生活状态的消费者欢迎。巨大的需求与发展空间一定会让各种类型的家用按摩机械发展势头强劲,成为健康产业的新的增长点。在丰厚的利润和发展空间的吸引下,我国的不少企业纷纷涉足按摩器具生产领域。因社会节奏和工作压力的逐渐增大,人民需要放松自己来缓减工作的压力和身心疲惫,因此各种人体保健机械走入大众家庭。人民在长时间的看书、写字或在电脑前工作时,常常感到颈部肌肉酸痛,严重时还导致颈部不能转动,领用户甚为烦恼和痛苦,因此,颈部按摩器应运而生。目前,对于人体的颈部的按摩主要有人为按摩和机械按摩,人为按摩为人工劳动,其受人为因素影响较大,按摩质量上很难到达保证,而且需要到制定的服务场所,且还需要有专业的人士方可,不能随时随地的按摩,便携式的颈部按摩仪产品继而产生。
本设计主要针对长时间伏案工作的人,尤其是电脑使用者。颈部按摩器可在工作时使用,要求安全、轻便、舒适;由于是针对颈部,因此按摩器工作时还必须轻柔、舒缓,且不影响使用者低头工作。
本设计主要解决的技术问题是提供一种基于单片机控制的颈部按摩仪,其结构简单,按摩电机通过不同信号实现不同按摩模式,振动电机根据供电开关频率的不同实现振动的不同模式,加热模块根据检测的温度值进行升降温;根据按键输入的不同模式,控制模块输出不同的信号控制相应的电机实现不同的按摩模式。
技术要求:
(1)按摩仪热敷恒温最高50℃。
(2)恒温温度范围40~50℃,恒温温度控制精度±1℃。
(3)通过直流电机实现推拿、针刺、捶打三种按摩模式。
(4)控制振动电机的频率实现高、中、低三挡力度。
热敷、按摩的时间分为固定时长30min、45min、60min或自己设置任意时长
本设计采用模块化设计法,以51单片机为核心设计的一款颈部按摩仪,当按摩仪放置在脖颈上时,温度传感器将物体的温度转换成一定函数关系的电信号。该电信号先通过前端信号处理电路,然后经过A/D转换电路转换成数字信号送入到主控电路的单片机中,单片机通过扫描键盘和各种功能开关,根据键盘输入内容和各种功能开关的状态进行判断、分析和控制,来完成按摩、加热以及显示功能。
其硬件结构主要包括:温度检测模块、输入模块、控制模块、电源模块、按摩模块、加热模块和显示模块。其中,最小系统部分主要包括STC89C52RC、晶振电路和复位电路;键盘由4X4位矩阵键盘组成,可以选择不同的功能、控制按摩力度及按摩时间;显示部分由OLED组成。软件部分由C语言编程,实现对各部分的控制。
按摩功能实现:由单片机发出三种波形(正弦波、方波、三角波)驱动电机,实现对人体的多种按摩模式。
热敷功能实现:经过三极管控制加热片,当其导通后,电阻丝发热。
温度检测功能:由DS18B20传入数据后进行分析,得到精确到每一摄氏度的当前温度。
按键交互:单片机不断检测矩阵键盘的被按下的键值,调整系统的时间、温度档位等。
屏幕显示:在OLED屏幕上显示当前按摩力度档位、按摩和热敷剩余时间、按摩模式、当前温度。
设计一个颈部按摩仪,即基于单片机的多功能按摩仪系统。该系统包括直流电机、振动电机、温度传感器、矩阵按键、加热片、OLED屏幕多个部分。
由DS18B20与矩阵按键向单片机内传入数据或触发程序中断,经过内部处理后对直流电机、振动电机、加热片和OLED屏幕实现不同的控制和显示功能,以完成本次设计内容。
2.2总流程图
当上电开始运行时,进入模式选择界面,此时按键被轮流扫描,按下某个按键时,单片机获取该按键的键值进行分析,判断是选择的哪个模式,并继续按照该模式对应的程序继续运行。
(1)按摩功能
在按摩模式下,首先会进行按摩方式的选择,通过对不同方式的选择,单片机会发出对应的正弦波、方波、三角波去驱动电机,来完成对脖颈的按摩放松。不同类型的波形会产生针刺、捶打、推拿三种形式的按摩;接着,对按摩力度的选择,有高、中、低三档力度,使用者可根据自己的喜好来进行选择。
按摩模式选择完后会进行时间的选择,可以选择固定的时长30min、45min、60min,也可以自己设置任意时长,当按下开始键后,进入按摩状态,屏幕实时显示当前剩余时间,直至中途按下停止按键或到达设定时长自动停止。
(2)热敷功能
在热敷模式下,首先进入对温度的选择,初始默认是40℃,最高温度是50℃,用户可根据个人需求使用按键对设定温度进行调整;选择温度后进入时间的选择,默认可选30/45/60min,也可自由任意设置时间,按下开始按键后,进入热敷状态,屏幕将会显示当前剩余时间与当前温度,中途按下停止键或时间到达会自动停止运行。
在热敷状态中,如果温度低于设定的温度区间,单片机将会控制加热片开始工作,实现对按摩仪的审问,直至满足要求,以此往复尽量保持在温度区间,实现动态调节保持恒温。
#include< reg51.h > #define uchar unsigned char #define uint unsigned int sbit DQ=P1^3; //数据传输线接单片机的相应的引脚 unsigned char tempL=0; //设全局变量 unsigned char tempH=0; unsigned int sdata; //测量到的温度的整数部分 unsigned char xiaoshu1; //小数第一位 unsigned char xiaoshu2; //小数第二位 unsigned char xiaoshu; //两位小数 bit fg=1; //温度正负标志 sbit wr=P2^0; //数据写 sbit rd=P2^1; //数据读 sbit ce=P2^2; //片选 sbit cd=P2^3; //指令数据通道,1指令,0数据 sbit rst=P2^4; //复位信号 sbit LED = P1^0; sbit MOT = P1^1; sbit BEEP = P1^2; sbit KEY1 = P3^0; sbit KEY2 = P3^1; sbit KEY3 = P3^2; sbit KEY4 = P3^3; sbit KEY5 = P3^4; sbit KEY6 = P3^5; sbit KEY7 = P3^6; uchar Set_temp = 45; uchar Set_mode = 1; uchar Set_power = 1; uchar Sec = 0; uchar Min = 20; uchar flag; void delay_18(unsigned char i) { for(i;i >0;i--); } void delay_ms(uint c) //误差 0us { uchar a,b; for (; c >0; c--) { for (b=199;b >0;b--) { for(a=1;a >0;a--); } } } uchar code HZ0[][32]={ {0x00,0x00,0x7E,0xFE,0x04,0x10,0x08,0x20,0x18,0x7C,0x24,0x44,0x42,0x54,0x81,0x54, 0x00,0x54,0x7E,0x54,0x08,0x54,0x08,0x54,0x08,0x28,0x0E,0x24,0xF0,0x42,0x40,0x82},/*"颈",0*/ {0x10,0x00,0x08,0x3E,0x7F,0xA2,0x00,0x24,0x21,0x24,0x12,0x28,0xFF,0xE4,0x00,0x24, 0x00,0x22,0x3F,0x22,0x21,0x22,0x21,0x34,0x21,0x28,0x3F,0x20,0x21,0x20,0x00,0x20},/*"部",1*/ {0x10,0x40,0x10,0x20,0x10,0x20,0x13,0xFE,0xFA,0x02,0x14,0x44,0x10,0x40,0x1B,0xFE, 0x30,0x88,0xD0,0x88,0x11,0x08,0x10,0xD0,0x10,0x20,0x10,0x50,0x50,0x88,0x23,0x04},/*"按",2*/ {0x00,0x80,0x3F,0xFE,0x24,0x10,0x3F,0x7C,0x26,0x38,0x2D,0x54,0x34,0x12,0x20,0x38, 0x27,0xC0,0x20,0x40,0x27,0xF8,0x20,0x40,0x2F,0xFE,0x40,0x40,0x41,0x40,0x80,0x80},/*"摩",3*/ {0x08,0x80,0x08,0x48,0x0A,0x48,0x12,0x08,0x12,0x08,0x31,0x10,0x31,0x10,0x51,0x10, 0x90,0xA0,0x10,0xA0,0x10,0x40,0x10,0x40,0x10,0xA0,0x11,0x10,0x12,0x08,0x14,0x06},/*"仪",4*/ {0x01,0x00,0x21,0x08,0x11,0x08,0x09,0x10,0x09,0x20,0x01,0x00,0x7F,0xF8,0x00,0x08, 0x00,0x08,0x00,0x08,0x3F,0xF8,0x00,0x08,0x00,0x08,0x00,0x08,0x7F,0xF8,0x00,0x08},/*"当",5*/ {0x10,0x10,0x08,0x10,0x08,0x20,0xFF,0xFE,0x00,0x00,0x3E,0x08,0x22,0x48,0x22,0x48, 0x3E,0x48,0x22,0x48,0x22,0x48,0x3E,0x48,0x22,0x08,0x22,0x08,0x2A,0x28,0x24,0x10},/*"前",6*/ {0x10,0x20,0x11,0x24,0x10,0xA4,0x10,0xA4,0xFC,0xA8,0x10,0x20,0x33,0xFC,0x38,0x04, 0x54,0x04,0x50,0x04,0x91,0xFC,0x10,0x04,0x10,0x04,0x10,0x04,0x13,0xFC,0x10,0x04},/*"档",7*/ {0x08,0x80,0x08,0x40,0x08,0x40,0x10,0x00,0x17,0xFC,0x30,0x00,0x30,0x08,0x52,0x08, 0x92,0x08,0x11,0x10,0x11,0x10,0x11,0x10,0x11,0x20,0x10,0x20,0x1F,0xFE,0x10,0x00},/*"位",8*/ {0x00,0x00,0x23,0xF8,0x12,0x08,0x12,0x08,0x83,0xF8,0x42,0x08,0x42,0x08,0x13,0xF8, 0x10,0x00,0x27,0xFC,0xE4,0xA4,0x24,0xA4,0x24,0xA4,0x24,0xA4,0x2F,0xFE,0x00,0x00},/*"温",9*/ {0x01,0x00,0x00,0x80,0x3F,0xFE,0x22,0x20,0x22,0x20,0x3F,0xFC,0x22,0x20,0x22,0x20, 0x23,0xE0,0x20,0x00,0x2F,0xF0,0x24,0x10,0x42,0x20,0x41,0xC0,0x86,0x30,0x38,0x0E},/*"度",10*/ {0x11,0x10,0x11,0x10,0x17,0xFC,0x11,0x10,0xFC,0x00,0x13,0xF8,0x32,0x08,0x3B,0xF8, 0x56,0x08,0x53,0xF8,0x90,0x40,0x17,0xFC,0x10,0xA0,0x11,0x10,0x12,0x08,0x14,0x06},/*"模",11*/ {0x00,0x48,0x00,0x44,0x00,0x44,0x00,0x40,0xFF,0xFE,0x00,0x40,0x00,0x40,0x3E,0x40, 0x08,0x40,0x08,0x40,0x08,0x20,0x08,0x22,0x0F,0x12,0x78,0x0A,0x20,0x06,0x00,0x02},/*"式",12*/ {0x00,0x00,0x21,0xF0,0x11,0x10,0x11,0x10,0x01,0x10,0x02,0x0E,0xF4,0x00,0x13,0xF8, 0x11,0x08,0x11,0x10,0x10,0x90,0x14,0xA0,0x18,0x40,0x10,0xA0,0x03,0x18,0x0C,0x06},/*"设",13*/ {0x02,0x00,0x01,0x00,0x7F,0xFE,0x40,0x02,0x80,0x04,0x00,0x00,0x3F,0xF8,0x01,0x00, 0x01,0x00,0x11,0x00,0x11,0xF8,0x11,0x00,0x11,0x00,0x29,0x00,0x47,0xFE,0x80,0x00},/*"定",14*/ }; uchar code SZ[][16]={ {0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x24,0x18,0x00,0x00},/*"0",0*/ {0x00,0x00,0x00,0x08,0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x3E,0x00,0x00},/*"1",1*/ {0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x02,0x04,0x08,0x10,0x20,0x42,0x7E,0x00,0x00},/*"2",2*/ {0x00,0x00,0x00,0x3C,0x42,0x42,0x02,0x04,0x18,0x04,0x02,0x42,0x42,0x3C,0x00,0x00},/*"3",3*/ {0x00,0x00,0x00,0x04,0x0C,0x0C,0x14,0x24,0x24,0x44,0x7F,0x04,0x04,0x1F,0x00,0x00},/*"4",4*/ {0x00,0x00,0x00,0x7E,0x40,0x40,0x40,0x78,0x44,0x02,0x02,0x42,0x44,0x38,0x00,0x00},/*"5",5*/ {0x00,0x00,0x00,0x18,0x24,0x40,0x40,0x5C,0x62,0x42,0x42,0x42,0x22,0x1C,0x00,0x00},/*"6",6*/ {0x00,0x00,0x00,0x7E,0x42,0x04,0x04,0x08,0x08,0x10,0x10,0x10,0x10,0x10,0x00,0x00},/*"7",7*/ {0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x24,0x18,0x24,0x42,0x42,0x42,0x3C,0x00,0x00},/*"8",8*/ {0x00,0x00,0x00,0x38,0x44,0x42,0x42,0x42,0x46,0x3A,0x02,0x02,0x24,0x18,0x00,0x00},/*"9",9*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",10*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00},/*":",11*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"-",12*/ }; void delay(uint t) //延时函数 { uint i,j; for(i=0;i< 1;i++); } uchar read_status() //读取lcd12864的状态 { uchar status; P0=0;//端口b置为输入 rd=0; wr=1; ce=0; cd=1; status=P1; return status; } void check_status()//检查lcd12864的状态 { uchar s; P0=0Xff;//端口b置为输出 while((s&0x80)!=0x80) s=read_status();//等待位1,2置为。命令读写准备好。数据读写准备好 } void write_data(uchar dat)//写数据 { rd=1; cd=0; ce=0; wr=0; P0=dat; delay(1); wr=1; ce=1; cd=1; } void write_cmd1(uchar cmd)//写数据1 { rd=1; cd=1; ce=0; wr=0; P0=cmd; delay(1); wr=1; ce=1; cd=0; } //先送参数,再送指令 void write_cmd2(uchar dat,uchar cmd) { check_status(); write_data(dat); check_status(); write_cmd1(cmd); } void write_cmd3(uchar data1,uchar data2,uchar cmd)//发送参数并发指令再发送数据 { check_status(); write_data(data1); check_status(); write_data(data2); check_status(); write_cmd1(cmd); } void init()//LCD12864的初始化 { rst=1; delay(10); rst=0; wr=1; rd=1; ce=1; cd=1; rst=1; check_status(); write_cmd3(0x01,0x00,0x21);//光标指针设置 check_status(); write_cmd3(0x00,0x00,0x42);//图形区首地址 check_status(); write_cmd3(16,0x00,0x43);//图形区宽度 check_status(); write_cmd1(0x80);//显示方式设置,正常显示 check_status(); write_cmd1(0x98);//图形方式显示,不显示字母,只打点 check_status(); write_cmd1(0xa0);//光标形状设置1 0 1 0 0 N2 N1 N0 } //汉字显示函数,处在x y处显示汉字 void display_HZ(uchar x,uchar y,uchar *hz)////x 0-3 y 0-7 { uchar i,j=0; for(i=0;i< 16;i++) { write_cmd3(((j/2)< <4)|(y*2),x,0x24);//地址指针设置..低地址,高地址,命令 write_cmd2(hz[j++],0xc0); write_cmd2(hz[j++],0xc0); } } void display_SZ(uchar x,uchar y,uchar *hz)////x 0-3 y 0-14 { uchar i,j=0; for(i=0;i< 16;i++) { write_cmd3(((j)< <4)|y,x,0x24);//地址指针设置..低地址,高地址,命令 write_cmd2(hz[j++],0xc0); } } void CLear_N(uchar num) { uchar i; for(i=0;i< 15;i++) { display_HZ(num,i,SZ[10]); } } //display_SZ(0,0,SZ[0]); //display_HZ(0,0,HZ0[0]); void Init_DS18B20(void) { unsigned char x=0; DQ=1; //DQ先置高 delay_18(8); //稍延时 DQ=0; //发送复位脉冲 delay_18(80); //延时( >480us) DQ=1; //拉高数据线 delay_18(5); //等待(15~60us) x=DQ; //用X的值来判断初始化有没有成功,18B20存在的话X=0,否则X=1 delay_18(20); } //读一个字节 ReadOneChar(void) //主机数据线先从高拉至低电平1us以上,再使数据线升为高电平,从而产生读信号 { unsigned char i=0; //每个读周期最短的持续时间为60us,各个读周期之间必须有1us以上的高电平恢复期 unsigned char dat=0; for (i=8;i >0;i--) //一个字节有8位 { DQ=1; delay_18(1); DQ=0; dat >>=1; DQ=1; if(DQ) dat|=0x80; delay_18(4); } return(dat); } //写一个字节 void WriteOneChar(unsigned char dat) { unsigned char i=0; //数据线从高电平拉至低电平,产生写起始信号。15us之内将所需写的位送到数据线上, for(i=8;i >0;i--) //在15~60us之间对数据线进行采样,如果是高电平就写1,低写0发生。 { DQ=0; //在开始另一个写周期前必须有1us以上的高电平恢复期。 DQ=dat&0x01; delay_18(5); DQ=1; dat >>=1; } delay_18(4); } //读温度值(低位放tempL;高位放tempH;) void ReadTemperature(void) { Init_DS18B20(); //初始化 WriteOneChar(0xcc); //跳过读序列号的操作 WriteOneChar(0x44); //启动温度转换 delay_18(125); //转换需要一点时间,延时 Init_DS18B20(); //初始化 WriteOneChar(0xcc); //跳过读序列号的操作 WriteOneChar(0xbe); //读温度寄存器(头两个值分别为温度的低位和高位) tempL=ReadOneChar(); //读出温度的低位LSB tempH=ReadOneChar(); //读出温度的高位MSB if(tempH >0x7f) //最高位为1时温度是负 { tempL=~tempL; //补码转换,取反加一 tempH=~tempH+1; fg=0; //读取温度为负时fg=0 } sdata = tempL/16+tempH*16; //整数部分 xiaoshu1 = (tempL&0x0f)*10/16; //小数第一位 xiaoshu2 = (tempL&0x0f)*100/16%10; //小数第二位 xiaoshu=xiaoshu1*10+xiaoshu2; //小数两位 } void KEY_scan() { if(KEY1 == 0) { delay(20); if(KEY1 == 0) { while(KEY1 == 0); Set_temp++; } } if(KEY2 == 0) { delay(20); if(KEY2 == 0) { while(KEY2 == 0); Set_temp--; } } if(KEY3 == 0) { delay(20); if(KEY3 == 0) { while(KEY3 == 0); if(Set_mode != 3) { Set_mode++; }else { Set_mode = 0; } } } if(KEY4 == 0) { delay(20); if(KEY4 == 0) { while(KEY4 == 0); if(Set_power != 3) { Set_power++; }else { Set_power = 0; } } } if(KEY5 == 0) { delay(20); if(KEY5 == 0) { while(KEY5 == 0); if(flag==0) { Min++; } } } if(KEY6 == 0) { delay(20); if(KEY6 == 0) { while(KEY6 == 0); if(flag == 0) { Min--; } } } if(KEY7 == 0) { delay(20); if(KEY7 == 0) { while(KEY7 == 0); if(flag==0) { flag=1; MOT = 1; }else { flag = 0; MOT = 0; LED = 0; Sec = 0; Min = 20; } } } } void main() { TMOD |= 0x01; //开启定时器0 0x10时使用定时器1 0x11时启动两个 TH0=0XFC; //给定时器赋初值,定时1ms TH1 TL0=0X18; //TL1 EA=1; //总中断打开 ET0=1; //定时器0中断打开 ET1 TR0=1; //定时器0开关打开 TR1 init(); Init_DS18B20(); display_HZ(0,0,HZ0[0]); display_HZ(0,1,HZ0[1]); display_HZ(0,2,HZ0[2]); display_HZ(0,3,HZ0[3]); display_HZ(1,0,HZ0[5]); display_HZ(1,1,HZ0[6]); display_HZ(1,2,HZ0[9]); display_HZ(1,3,HZ0[10]); display_SZ(1,8,SZ[11]); display_SZ(1,11,SZ[12]); display_HZ(2,0,HZ0[5]); display_HZ(2,1,HZ0[6]); display_HZ(2,2,HZ0[11]); display_HZ(2,3,HZ0[12]); display_SZ(2,8,SZ[11]); display_HZ(3,0,HZ0[5]); display_HZ(3,1,HZ0[6]); display_HZ(3,2,HZ0[7]); display_HZ(3,3,HZ0[8]); display_SZ(3,8,SZ[11]); display_SZ(0,13,SZ[11]); LED = 0; MOT = 0; BEEP = 1; while(1) { delay_ms(10); ReadTemperature(); delay_ms(10); display_SZ(1,9,SZ[sdata/10]); display_SZ(1,10,SZ[sdata%10]); display_SZ(1,12,SZ[Set_temp/10]); display_SZ(1,13,SZ[Set_temp%10]); display_SZ(2,9,SZ[Set_mode]); display_SZ(3,9,SZ[Set_power]); display_SZ(2,9,SZ[Set_mode]); display_SZ(3,9,SZ[Set_power]); display_SZ(0,11,SZ[Min/10]); display_SZ(0,12,SZ[Min%10]); display_SZ(0,14,SZ[Sec/10]); display_SZ(0,15,SZ[Sec%10]); KEY_scan(); if(sdata < Set_temp && flag == 1) { LED = 1; }else if(sdata > Set_temp && flag == 1) { LED = 0; } } } void Timer0() interrupt 1 //参考上图 定时器1时为3 { static uint i; //定义一个自变形变量 TH0=0XFC; //给定时器赋初值,定时1ms TH1 TL0=0X18; //TL1 i++; if(i==1000) { i=0; if(flag == 1) { if(Sec != 0 && Min != 0) { Sec--; }else if(Sec == 0 && Min != 0) { Sec = 59; Min--; }else if(Sec == 0 && Min == 0) { flag = 0; BEEP = 0; MOT = 0; } } } }
全部0条评论
快来发表一下你的评论吧 !