综合项⽬——智能分类垃圾桶
综合项⽬——智能分类垃圾桶
⼀、讲在前⾯
之前做过许多项⽬,也写了许多⼯程代码,但是⼀直没能好好整理,导致我每做⼀个新的项⽬就跟重头开始似的,为了更好进⾏代码资料的管理,我决定开辟这个博客,作为我资料整理的开始。
-------------------------------------------分割线---------------------------------------
最近刚结束了⼀个特别折磨⼈的项⽬设计,这个项⽬设计的题⽬是智能分类垃圾桶,其中需要进⾏机械设计制图、安卓开发板代码编写、FPGA代码设计、MCU代码设计等等。我在其中负责FPGA开发板代码和STM32开发板代码的编写,秉承记录的⽬的和开源的初⼼,我将更新⼀个系列的博⽂进⾏MCU端代码和FPGA端代码的介绍,供⼤家⼀起学习进步,不⾜之处望补正。
⼆、博⽂介绍
在该系列博⽂,我会按照MCU代码和FPGA代码的顺序进⾏介绍,此次介绍MCU端的代码。
三、MCU代码框架
3.1代码框架分析
此次项⽬选择的控制平台是意法半导体出产的stm32L476RGT6系列的集成控制主板。板上集成了ST-LINK烧录器、Arduino接⼝等资源,引出了I2C、SPI、串⼝、ADC等管脚资源(正经⼈谁⽤硬件I2C和SPI不是),可谓是资源丰富,但是⼜有许多⽆⽤之处。
我们的MCU需要做的就是利⽤以上的板载资源,设计⼀个控制系统、对整个智能分类垃圾桶系统进⾏控制,这个控制系统应该包含以下⼏个功能:
(1)与安卓开发板进⾏通信;
(2)与FPGA开发板进⾏通信;
(3)读取TCS34725颜⾊传感器数据和VL6180距离传感器数据;
(4)向⼴和通L610物联⽹通信主板发送AT指令,进⽽向阿⾥云发送垃圾桶事件;
(5)⽤户服务程序,⽤于对⽤户预留的接⼝,⽤户在该任务⾥对垃圾桶的逻辑进⾏设计;
(6)开发者调试,串⼝打印垃圾桶属性和数据任务,⽤于垃圾桶维修者对垃圾桶进⾏维护。
3.2代码框架设计
根据以上的分析,我们需要实现6个任务,其中5个为常⽤任务,⼀个为调试维护任务。为此,我在我的MCU代码框架中开辟6个任务,对整个控制系统进⾏管理。下⾯附上⼀张代码框架图,有需要可以拷贝,但是请标注来源或者⾃⼰根据我的框架搭建⼀个类似的:爱在那一天演员表
在上⾯介绍中,我提到了任务管理的概念,那么我运⽤的任务管理⽅法是什么呢?没错,聪明的你⼀定猜到了,我运⽤的是时间⽚的管理⽅法。在代码中,我开启定时器3作为任务管理的时钟,对任务资源进⾏调度,对时间⽚管理不熟悉的伙伴可以⾃⾏百度哦。也许有⼈会问,为什么不运⽤操作系统进⾏任务的管理。emmm…其实刚开始的时候是有⽤FreeRtos编写的版本的啦,不过效果不好,被我摒弃了。如果有想要的伙伴,我根据反响,考虑专门出⼀篇博⽂介绍FreeRtsos系统下的任务管理介绍,欢迎⼤家在评论区积极发⾔哦!⾏了,扯了这么多蛋,接下来该进⼊正题了——各个任务代码的介绍。
四、各部分代码详解
4.1代码主循环
以下的主循环中运⾏着整个系统的全部任务,函数中的参数代表任务循⾏的频率的倒数,单位时ms。
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
data_read_task(5);  //传感器数据读取
fpga_com_task(10);  //与FPGA通信
Android_com_task(60);  //与上位机通信
L610_task(200);    //与L610通信
user_service_task(10);  //⽤户服务程序
#if DBUG
dbug_task(100);  //开发者调试
#endif
}
4.2传感器数据读取任务
我们需要读取的传感器数据有两种,TCS34725颜⾊传感器数据和VL6180距离传感器数据。这两个传感器数据的读取协议采⽤IIC(不了解IIC的同学这边建议百度),具体初始化和读取⽅法⽹上有许多例程,我在这⾥不再赘述,只挑选⼏个⽐较重要的函数进⾏讲解,下⾯贴上代码:
4.2.1传感器初始化
//传感器初始化
void sensor_init()
{
//读取TOF模块ID
tof_name =VL6180X_Read_ID();//读取TOF模块的ID
while(tof_name !=0xBE)//根据模块不同型号和焊接⼯艺选择ID
tof_name =VL6180X_Read_ID();//循环读取TOF模块的ID
init_flag =VL6180X_Init();//tof模块的初始化
HAL_Delay(300);//延时⼀段时间等待初始化完成
TCS34725_Setup();//颜⾊识别模块设置
TCS34725_Enable();//颜⾊识别模块使能
TCS34725_Read(TCS34725_ID,&TSC_name,1);//读取颜⾊识别模块ID
while(TSC_name !=0x44)
TCS34725_Read(TCS34725_ID,&TSC_name,1);//循环读取颜⾊识别模块的ID
}
这⾥需要注意的是传感器的ID,我⽤的距离传感器的ID是0XBE,别的同系列的距离传感器数据有0XB4的,如果程序卡在这个初始化循环⾥有可能是传感器ID设置的问题,对其进⾏修改即可。颜⾊传感器ID没什么⼤问题。代码注释在上⾯都有。
4.2.2传感器数据读取主体函数
由于我的整个程序运⽤的是时间⽚的管理⽅法,因此在进⾏函数的封装时,特别要注意的⼀个参数时任务运⾏的事件,原则上给定的任务运⾏时间只要⼤于任务的实际运⾏时间即可,但出于对各个任务实时性需求的不同,我们⼀般会把实时性需求⽐较⼤的任务给的运⾏频率更⼤。传感器数据更新实时性要求⽐较⾼,我们给定的运⾏频率⽐较⼤,有200HZ。
//传感器数据读取任务,频率200HZ
void data_read_task(uint32_t period)
{
长安汽车suv所有车型if(task_count[DATA_READ_TASK]>= period){
range =VL6180X_Read_Range();//读取距离数据
HAL_Delay(1);//延时等待
light =VL6180X_Read_Lux(VL6180X_ALS_GAIN_1_25);//读取光强数据
HAL_Delay(1);//延时等待
TCS34725_GetRawData(&my_color.CLE,&my_color.RED,&my_color.GRE,&my_color.BLU);//读取颜⾊传感器的颜⾊数据
HAL_Delay(1);//延时等待
voice_range =soner_getdistance();//超声波距离数据获取
task_count[DATA_READ_TASK]=0;
}
}
4.3⼴和通L610通信模块通信任务
⼴和通通信模块遵循的是TCP传输协议,我们通过串⼝1向物联⽹通信主板发送AT指令,可以控制MCU与阿⾥云云端的通信(阿⾥云与⼴和通L610通信模块的相关使⽤如果有不清楚的根据反响将出⼀篇博客介绍)。内容⽐较简单,就是串⼝发送相关AT指令,值得注意的⼏个点就是,在发送的数据结尾加上回车和换⾏,在代码中体现为\r\n,还有就是字符串中的双引号注意要⽤ASSIC的表达⽅式,正常双引号表达⽅式是“ ,ASSIC的表达⽅式是\ 加上"。
4.3.1⼴和通通信模块连接阿⾥云配置
int K ;
void L610_init()
{
//配置阿⾥云服务器
char*strx;
Config_para();//配置阿⾥云服务参数
strx =strstr((const char*)RECEIVE,(const char*)"OK");
while(strx ==NULL)
{
K++;
if(K>=6)//防⽌程序卡死,配置命令发送超过6次之后跳出初始化循环
{
break;
}
Config_para();//配置阿⾥云服务参数
strx =strstr((const char*)RECEIVE,(const char*)"OK");
HAL_Delay(300);
}
/
/以下为oled数据显⽰配置,可以选择注释
//  if(K>=6)
//  {
//  OLED_Clear_part();
//  OLED_ShowString(20,5,(uint8_t *)"L610 ERROR!",16 );
母亲节该送什么礼物
//  HAL_Delay(3000);
//  }
//  else
//  {
//  OLED_Clear_part();
//  OLED_ShowString(16,5,(uint8_t *)"L610 SUCCESS!",16 );
/
/  HAL_Delay(3000);
//
doctor异乡人张亮
//  }
}
4.3.2⼴和通物联⽹通信模块运⾏主体函数
以下代码块不仅包含⼴和通的通信部分,还包含oled的刷新部分。
int j;
void L610_task(uint32_t peirod)
{
if(task_count[L610_TASK]>= peirod){
Config_para();//配置阿⾥云服务参数
/
/垃圾桶各项属性和数据显⽰
if(frame ==1)
{
OLED_Clear_part();
OLED_ShowString(5,5,(uint8_t *)"type :",8);
OLED_ShowString(5,7,(uint8_t *)"state:",8);
if(sevo_step ==1)
OLED_ShowString(60,7,(uint8_t *)"complete",8);
else
OLED_ShowString(60,7,(uint8_t *)"onging",8);
if(RX_DATA_FROM_FPGA =='1')
{
OLED_ShowString(60,5,(uint8_t *)"RB1",8);
}
else if(RX_DATA_FROM_FPGA =='2')
{
OLED_ShowString(60,5,(uint8_t *)"RB2",8);
}
else if(RX_DATA_FROM_FPGA =='3')
{
OLED_ShowString(60,5,(uint8_t *)"RB3",8);
}
else if(RX_DATA_FROM_FPGA =='4')
{
OLED_ShowString(60,5,(uint8_t *)"RB4",8);
}
else
{
OLED_ShowString(60,5,(uint8_t *)"NULL",8);
}
frame =-frame;
}
else
{
OLED_Clear_part();
OLED_ShowString(5,5,(uint8_t *)"range:",8);
OLED_ShowNum(60,5,range,3,8);
OLED_ShowString(5,7,(uint8_t *)"remain:",8);
OLED_ShowNum(60,7,voice_range,3,8);
frame =-frame;
}
//超声波数据作为垃圾余量的参考数据
if(voice_range<=6)
{
OLED_Clear_part();
OLED_ShowString(6,5,(uint8_t *)"full sent:",8);物流成本管理
Send_data((char*)"\"hello\"");
}
//上位机发送呼叫管理员指令
if(AS_call_manager ==1)
{
OLED_Clear_part();
OLED_ShowString(6,5,(uint8_t *)"calling sent:",8);
for(j=0;j<=20;j++)
Send_data_call((char*)"\"hello\"");
AS_call_manager =0;
}
//⽤户模式和DBUG模式切换显⽰
if(dbug_flag ==0)
{
OLED_ShowString(92,2,(uint8_t *)"USER",8);
}
else
OLED_ShowString(92,2,(uint8_t *)"DBUG",8);
task_count[L610_TASK]=0;
}
}
以上就是此次的更新内容,剩余的与FPGA通信部分和与上位机的通信部分过于庞⼤,咱们下次介绍,这次先把代码贴上,有不清楚的可以下⾯评论留⾔哦。
上位机通信部分:主要是数据收发和协议约定
//与上位机通信,运⾏频率(1000/60)HZ
void Android_com_task(uint32_t peroid)
{
if(task_count[COM_TO_Andr]>= peroid)
{
//接收上位机数据
HAL_UART_Receive_IT(&huart4,&RX_DATA[0],1);
//根据上位机指令选择调试模式和⽤户模式
if(RX_DATA[0]=='G')
{
dbug_flag = Android_DBUG;
RX_DATA[0]=0;
}
else if(RX_DATA[0]=='U')
{
dbug_flag = Android_USER;
RX_DATA[0]=0;
}
range_temp =(uint8_t) voice_range;
if(dbug_flag == Android_USER)
{
if(range <=100)
{
TX_DATA[0]='l';
//    TX_DATA[1] = 1;
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
>李易峰的结婚照