往前走 发表于 2022-8-20 23:24:22

【开源】智能柜设计(包括设计方案与设计资料)

本帖最后由 往前走 于 2022-9-17 22:38 编辑

最近帮客户做了一款针对实验柜的舵机控制系统,本着完美主义,我在这里将用T5L屏设计一款高端大气时尚的智能柜管理系统。(资料陆续更新,欢迎大家提出批评建议)(dgus工程所用图片均来自于网络,仅供各位朋友交流参考,请勿商用)

首先说一下本智能的优势
1、兼容性:
   (1)液晶屏(操作面包)兼容性:在设计上采用了T5L标准屏作为主控,直接驱动串行总线舵机,传感器汇总到辅助控制器(如STC系列单片机),再将数据通过串口4发送给液晶屏,在实际开发过程中,只需要根据采用的液晶屏的分辨率改变DGUS工程就行。
   (2)舵机兼容性:采用飞特的STS系列串行总线舵机作为驱动,这类舵机的力矩涵盖了4.5KG到40KG,且它们的协议通用
2、安全性:串行总线舵机具有电流、力矩、温度、电压保护功能,其使用安全性要高于常规的电机。
3、可拓展性:串行总线舵机,理论上来说,一个串口支持同时控制254个舵机,即一块液晶屏作为主控,可以带动254个舵机。

以下是设计资料(资料随时更新)

演示视频:链接: https://pan.baidu.com/s/19_-gfqtCd4SaAHN9-SsLMQ?pwd=4967 提取码: 4967 复制这段内容后打开百度网盘手机App,操作更方便哦
关于演示视频的几点说明:
1.视频中采用的舵机为飞特SCS115型与SCS40两种型号的舵机,电源为24V开关电源,经,降压至8-9V接入舵机
2.DGUS工程为800*480的,但我的80480的屏摔坏了,所以用的7寸1024*600的液晶屏
3.由于论坛里有大神已经做了时间更新与flash操作的代码(迪文温控器代码),所以我就不做演示
4.关于舵机动作顺序,开门:门栓舵机动作完成,开门舵机再动作;关门:开门舵机动作完成,然后门栓舵机再动作

如下图所示,为本文设计的智能柜的框架,迪文屏作为主控制器,通过串口2直接输出指令控制串行总线舵机,串口4与辅助控制器连接,用于拓展传感器,例如用于化学实验柜,可以拓展用以检测温度、气体、火焰检测等传感器。灯光系统用于智能柜内部照明使用,采用行程开关与光耦传感器共同控制,即在照明不良、柜门打开时,点亮照明灯光,电路见下文。


为了防止停电时,智能柜门处于失控状态,本设计采用了双舵机的设计,即开门舵机负责柜门的开关动作,门闩舵机负责门闩动作,停电后,由于门闩的存在,即使开门舵机卸力,智能柜也依然处于上锁状态,当然,如果用户觉得双舵机的成本太高,可以选择不加门闩舵机,对本设计的系统运行并不产生影响。机械结构如图所示:



接下来说明一下本设计的程序框架,程序是C51语音,在液晶屏中运行

电路原理图分为三部分:主电路板(舵机驱动电路+辅助控制器+接口)、降压电路、灯光电路(安装在柜子里边)

我在这里使用STM32F103RCT6作为辅助控制器,单片机的串口1与液晶屏的串口4相连接,液晶屏串口2直接驱动舵机,同时设计有一路输入,一路输出,用于紧急情况

辅助控制器与传感器使用的电压多为5V,而舵机的电压直接与舵机型号相关,例如SCS115型的电压为7-8V,因此本设计采用了LM2596S-5.0输出5V电压,LM2596S-ADJ可调输出7.5V电压用于舵机,不同的电压,调节电阻R_ADJ的阻值即可。
由于使用的降压芯片为LM2596S,支持最大输入电压40V

这里采用了光耦电阻检测亮度,行程开关与柜门相接,当柜门打开时,行程开关导通,如果光线太暗,小灯泡就可以点亮。
降压板与驱动板安装在如下图所示的小盒子里(3D图纸见附件),底层为降压板,上层为主电路板,




1.温湿度检测与刷新,时间更新(由辅助控制器驱动AHT21,将温湿度数据写入迪文屏)
/*****************温湿度更新**********************/
void dwin_Tempe_humi_update( void)
{
      uint8_tTempe_humi_date;//发送给液晶屏的指令
      AHT20_Read_CTdata(CT_data);//读取温度和湿度
      
      Tempe_humi_date=0x5A;
      Tempe_humi_date=0xA5;
      Tempe_humi_date=0x07;
      Tempe_humi_date=0x82;
      Tempe_humi_date=(ADDR_TEMP_HUMI>>8)&0xff;
      Tempe_humi_date=ADDR_TEMP_HUMI&0xff;
      Tempe_humi_date=((CT_data *200*10/1024/1024-500)>>8)&0xff;
      Tempe_humi_date=((CT_data *200*10/1024/1024-500))&0xff;//计算得到温度值(放大了10倍,如果t1=245,表示现在温度为24.5℃)
      Tempe_humi_date=((CT_data*1000/1024/1024)>>8)&0xff;
      Tempe_humi_date=((CT_data*1000/1024/1024))&0xff;//计算得到湿度值(放大了10倍,如果c1=523,表示现在湿度为52.3%)
      Usart_SendString( USART_DWIN,Tempe_humi_date,10);

}

/*****************时间更新**********************/

uint8_tDGUS_RTC_date;
void dwin_time_update( u32 time_value)
{
      struct rtc_time time;
      unsigned int days,hour_min_sec;
      unsigned int i;
      
      days = time_value / 86400;//一天有86400秒
      hour_min_sec = time_value % 86400;//一天有86400秒
      
      time.hour = hour_min_sec / 3600;//一小时有3600秒
      time.mintute =( hour_min_sec % 3600 ) / 60;//一分钟有60秒
      time.second =( hour_min_sec % 3600 ) % 60;
      
      //年份判断,从1970年开始
      for ( i=1970;days>=days_in_years(i);i++)
      {
                days-=days_in_years(i);
      }
      time.year=i;
      //判断月份
      if (leapyear(i))
                days_in_month=29;
      else
                days_in_month=28;
                for ( i=1;days>=days_in_month;i++)
      {
                days-=days_in_month;
      }
      time.month = i;
      //日期计算
      time.day = days + 1 ;
      
      time.week=RTC_Get_Week(time.year,time.month, time.day);

      DGUS_RTC_date=0x5A;
      DGUS_RTC_date=0xA5;
      DGUS_RTC_date=0x18;
      DGUS_RTC_date=0x82;
      DGUS_RTC_date=(ADDR_RTC_TIME>>8)&0xff;
      DGUS_RTC_date=ADDR_RTC_TIME&0xff;
      DGUS_RTC_date=0x00;
      DGUS_RTC_date=time.hour;//时
      DGUS_RTC_date=0x00;
      DGUS_RTC_date=time.mintute;//分
      DGUS_RTC_date=00;
      DGUS_RTC_date=time.second;//秒
      DGUS_RTC_date=(time.year>>8)&0xff;//年
      DGUS_RTC_date=time.year&0xff;//年
      DGUS_RTC_date=00;
      DGUS_RTC_date=time.month;//月
      DGUS_RTC_date=00;
      DGUS_RTC_date=time.day;//日
      
      DGUS_RTC_date=ascii_8hex;//(time.week>>8)&0xff;//ascii_8hex[];//
      DGUS_RTC_date=ascii_8hex;// time.week&0xff;//当前星期
      

      Usart_SendString( USART_DWIN,DGUS_RTC_date,26);
      

}
//星期运算


uint16_t weeks(int weekday)
{
//      uint16_t asic_2_h;
      if(weekday>6)
                weekday-=7;

      return weekday;
}


(上传空间有限,需要详细资料的请邮箱联系1462818330@qq.com)





往前走 发表于 2022-8-20 23:54:22

本节主要是简单说一下飞特舵机指令协议,供大家做个参考。
飞特舵机STS系列采用TTL通信,指令格式为8-N-1,波特率默认1000000,可以更改为1000000,500000,250000,128000,115200,76800,57600,38400
1.指令
   字头   ID号 数据长度 指令    参数                                 校验和
0XFF 0XFF ID Length Instruction Parameter1 ...Parameter N Check Sum
字头:连续收到两个 0XFF ,表示有数据包到达。
ID: 每个舵机都有一个 ID 号。 ID 号范围 0~253,转换为十六进制 0X00~0XFD。
广播 ID: ID 号 254 为广播 ID,若控制器发出的 ID 号为 254(0XFE),所有的舵机均接收指令,除 PING 指令外其它指令均不返回应答信息(多个舵机连接在总线上不能使用广播 PING指令)。
数据长度:等于待发送的参数长度 N 加上 2,即“N+2”。
参数:除指令外需要补充的控制信息,参数最大支持双字节参数表示一个内存值,字节顺序参考舵机使用手册(不同型号舵机字节顺序不一样)。
校验和:校验和 Check Sum,计算方法如下:Check Sum = ~ (ID + Length + Instruction + Parameter1 + ... Parameter N)
若括号内的计算和超出 255,则取最低的一个字节,“~”表示取反。
例如,控制舵机转到1500位置,则指令为FF FF 01 0A 03 29 32 E8 03 00 00 E8 03 C0 ,该指令控制舵机位置与转动速度、加速度
2.应答
    字头      ID 数据长度 当前状态 参数                         校验和
0XFF 0XFF ID Length ERROR Parameter1 ...Parameter N Check Sum
返回的应答包包含舵机的当前状态 ERROR,若舵机当前工作状态不正常,会通过这个字节反映出来,若 ERROR 为 0,则舵机无报错信息。
若指令是读指令 READ DATA,则 Parameter1 ...Parameter N 是读取的信息。

详细舵机指令说明,大家可以自行查找官网。

往前走 发表于 2022-8-26 22:36:36

本帖最后由 往前走 于 2022-8-28 22:48 编辑

主要功能程序说明:2.舵机内存表宏定义(节选)
#define SCSCL_ID 5
#define SCSCL_BAUD_RATE 6
#define SCSCL_RETURN_DELAY_TIME 7
#define SCSCL_RETURN_LEVEL 8
#define SCSCL_MIN_ANGLE_LIMIT_L 9
#define SCSCL_MIN_ANGLE_LIMIT_H 10
#define SCSCL_MAX_ANGLE_LIMIT_L 11
#define SCSCL_MAX_ANGLE_LIMIT_H 12
#define SCSCL_LIMIT_TEMPERATURE 13
#define SCSCL_MAX_LIMIT_VOLTAGE 14
#define SCSCL_MIN_LIMIT_VOLTAGE 15
#define SCSCL_MAX_TORQUE_L 16
#define SCSCL_MAX_TORQUE_H 17
#define SCSCL_ALARM_LED 19
#define SCSCL_ALARM_SHUTDOWN 20
#define SCSCL_COMPLIANCE_P 21
#define SCSCL_COMPLIANCE_D 22
#define SCSCL_COMPLIANCE_I 23
#define SCSCL_PUNCH_L 24
#define SCSCL_PUNCH_H 25
#define SCSCL_CW_DEAD 26
#define SCSCL_CCW_DEAD 27
#define SCSCL_PROTECT_TORQUE 37
#define SCSCL_PROTECT_TIME 38
#define SCSCL_OVLOAD_TORQUE 39
#define SCSCL_TORQUE_ENABLE 40
#define SCSCL_GOAL_POSITION_L 42
#define SCSCL_GOAL_POSITION_H 43
#define SCSCL_GOAL_TIME_L 44
#define SCSCL_GOAL_TIME_H 45
#define SCSCL_GOAL_SPEED_L 46
#define SCSCL_GOAL_SPEED_H 47
#define SCSCL_LOCK 48

3.舵机服务子程序(其他子程序,由于篇幅有限,这里就不写)
int writePos(uint8_t ID, uint16_t Position, uint16_t Time, uint16_t Speed, uint8_t Fun)
{
      uint8_t buf;
      flushSCS();//清空缓冲区

      u16_to_u8(buf+0, buf+1, Position);
      u16_to_u8(buf+2, buf+3, Time);
      u16_to_u8(buf+4, buf+5, Speed);
      writeBuf(ID, SCSCL_GOAL_POSITION_L, buf, 6, Fun);
      return Ack(ID);
}

//写位置指令
//舵机ID,Position位置,运行时间Time,速度Speed
int WritePos(uint8_t ID, uint16_t Position, uint16_t Time, uint16_t Speed)
{
      return writePos(ID, Position, Time, Speed, INST_WRITE);
}

//异步写位置指令
//舵机ID,Position位置,运行时间Time,速度Speed
int RegWritePos(uint8_t ID, uint16_t Position, uint16_t Time, uint16_t Speed)
{
      return writePos(ID, Position, Time, Speed, INST_REG_WRITE);
}

void RegWriteAction(void)
{
      writeBuf(0xfe, 0, NULL, 0, INST_ACTION);
}

//写位置指令
//舵机ID[]数组,IDN数组长度,Position位置,运行时间Time,速度Speed
void SyncWritePos(uint8_t ID[], uint8_t IDN, uint16_t Position, uint16_t Time, uint16_t Speed)
{
      uint8_t buf;

      u16_to_u8(buf+0, buf+1, Position);
      u16_to_u8(buf+2, buf+3, 0);
      u16_to_u8(buf+4, buf+5, Speed);
      snycWrite(ID, IDN, SCSCL_GOAL_POSITION_L, buf, 6);
}

//读位置,超时返回-1
int ReadPos(uint8_t ID)
{
      return readWord(ID, SCSCL_PRESENT_POSITION_L);
}

//速度控制模式
int WriteSpe(uint8_t ID, int16_t Speed)
{
      if(Speed<0){
                Speed = -Speed;
                Speed |= (1<<10);
      }
      return writeWord(ID, SCSCL_GOAL_TIME_L, Speed);
}

//读负载,超时返回-1
int ReadLoad(uint8_t ID)
{      
      return readWord(ID, SCSCL_PRESENT_LOAD_L);
}

//读电压,超时返回-1
int ReadVoltage(uint8_t ID)
{      
      return readByte(ID, SCSCL_PRESENT_VOLTAGE);
}

//读温度,超时返回-1
int ReadTemper(uint8_t ID)
{      
      return readByte(ID, SCSCL_PRESENT_TEMPERATURE);
}

//Ping指令,返回舵机ID,超时返回-1
int Ping(uint8_t ID)
{
      int Size;
      uint8_t bBuf;
      flushSCS();
      writeBuf(ID, 0, NULL, 0, INST_PING);
      Size = readSCS(bBuf, 6);
      if(Size==6){
                return bBuf;
      }else{
                return -1;
      }
}

void writeBuf(uint8_t ID, uint8_t MemAddr, uint8_t *nDat, uint8_t nLen, uint8_t Fun)
{
      uint8_t i;
      uint8_t msgLen = 2;
      uint8_t bBuf;
      uint8_t CheckSum = 0;
      bBuf = 0xff;
      bBuf = 0xff;
      bBuf = ID;
      bBuf = Fun;
      if(nDat){
                msgLen += nLen + 1;
                bBuf = msgLen;
                bBuf = MemAddr;
                writeSCS(bBuf, 6);//MemAddr保存的是目标位置在舵机SRAM中的保存地址,如果有目标参数(判断nDat==1),则会从这一位开始输出

      }else{
                bBuf = msgLen;
                writeSCS(bBuf, 5);//如果没有目标参数(判断nDat==0)则不输出目标地址,也就只有五位数
      }
      CheckSum = ID + msgLen + Fun + MemAddr;
      if(nDat){
                for(i=0; i<nLen; i++){
                        CheckSum += nDat;
                }
                writeSCS(nDat, nLen);
      }
      CheckSum = ~CheckSum;
      writeSCS(&CheckSum, 1);
}

//普通写指令
//舵机ID,MemAddr内存表地址,写入数据,写入长度
int genWrite(uint8_t ID, uint8_t MemAddr, uint8_t *nDat, uint8_t nLen)
{
      flushSCS();
      writeBuf(ID, MemAddr, nDat, nLen, INST_WRITE);
      return Ack(ID);
}
//读指令
//舵机ID,MemAddr内存表地址,返回数据nData,数据长度nLen
int Read(uint8_t ID, uint8_t MemAddr, uint8_t *nData, uint8_t nLen)
{
      int Size;
      uint8_t bBuf;
      flushSCS();
      writeBuf(ID, MemAddr, &nLen, 1, INST_READ);
      if(readSCS(bBuf, 5)!=5){
                return 0;
      }
      Size = readSCS(nData, nLen);
      if(readSCS(bBuf, 1)){
                return Size;
      }
      return 0;
}



简言之 发表于 2022-10-9 09:06:57

太棒了,顶顶顶!!!

REXINXIAOSHIMIN 发表于 2023-12-29 09:10:49

你的3d模型用什么画的
页: [1]
查看完整版本: 【开源】智能柜设计(包括设计方案与设计资料)