综合项目——智能分类垃圾桶
一、讲在前面
之前做过许多项目,也写了许多工程代码,但是一直没能好好整理,导致我每做一个新的项目就跟重头开始似的,为了更好进行代码资料的管理,我决定开辟这个博客,作为我资料整理的开始。
-------------------------------------------分割线---------------------------------------
最近刚结束了一个特别折磨人的项目设计,这个项目设计的题目是智能分类垃圾桶,其中需要进行机械设计制图、安卓开发板代码编写、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_name = VL6180X_Read_ID();
while(tof_name != 0xBE)
tof_name = VL6180X_Read_ID();
init_flag = VL6180X_Init();
HAL_Delay(300);
TCS34725_Setup();
TCS34725_Enable();
TCS34725_Read(TCS34725_ID,&TSC_name,1);
while(TSC_name != 0x44)
TCS34725_Read(TCS34725_ID,&TSC_name,1);
}
这里需要注意的是传感器的ID,我用的距离传感器的ID是0XBE,别的同系列的距离传感器数据有0XB4的,如果程序卡在这个初始化循环里有可能是传感器ID设置的问题,对其进行修改即可。颜色传感器ID没什么大问题。代码注释在上面都有。
4.2.2传感器数据读取主体函数
由于我的整个程序运用的是时间片的管理方法,因此在进行函数的封装时,特别要注意的一个参数时任务运行的事件,原则上给定的任务运行时间只要大于任务的实际运行时间即可,但出于对各个任务实时性需求的不同,我们一般会把实时性需求比较大的任务给的运行频率更大。传感器数据更新实时性要求比较高,我们给定的运行频率比较大,有200HZ。
void data_read_task(uint32_t period)
{
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)
{
break;
}
Config_para();
strx = strstr((const char*)RECEIVE,(const char*)"OK");
HAL_Delay(300);
}
}
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;
}
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通信部分和与上位机的通信部分过于庞大,咱们下次介绍,这次先把代码贴上,有不清楚的可以下面评论留言哦。
上位机通信部分:主要是数据收发和协议约定
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';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
if(( RX_DATA[0] == 'b' || time_cmd == 1)&&(garbage_complete == 1) )
{
RX_DATA[0] = 0;
AS_cmd = 1;
}
if((RX_DATA[0] == 'a') && (AS_call_manager == 0))
{
AS_call_manager = 1;
RX_DATA [0] = 0;
}
if((garbage_complete == 0)&&(sevo_cmd_type == GARBAGE_1))
{
TX_DATA[0] = 'k';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
TX_DATA[0] = 61;
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
else if((garbage_complete == 0)&&(sevo_cmd_type == GARBAGE_2))
{
TX_DATA[0] = 'i';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
TX_DATA[0] = 62;
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
else if((garbage_complete == 0)&&(sevo_cmd_type == GARBAGE_3))
{
TX_DATA[0] = 'o';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
TX_DATA[0] = 63;
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
else if((garbage_complete == 0)&&(sevo_cmd_type == GARBAGE_4))
{
TX_DATA[0] = 'v';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
TX_DATA[0] = 64;
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
else
{
TX_DATA[0] = 't';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
}
else if(dbug_flag == Android_DBUG)
{
if(RX_DATA[0] == 'M')
{
RX_DATA[0] = 0;
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 126;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 144;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 21;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 15;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 9;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 15;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
}
else if(RX_DATA[0] == 'f')
{
if(range == 0)
TX_DATA[0] = 'h';
else
TX_DATA[0] = 'l';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
RX_DATA[0] = 0;
}
else if(RX_DATA[0] == 'g')
{
if(sevo_cmd_type == GARBAGE_1)
{
TX_DATA[0] = 'k';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
else if(sevo_cmd_type == GARBAGE_2)
{
TX_DATA[0] = 'i';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
else if(sevo_cmd_type == GARBAGE_3)
{
TX_DATA[0] = 'o';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
else if(sevo_cmd_type == GARBAGE_4)
{
TX_DATA[0] = 'v';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
else
{
TX_DATA[0] = 't';
HAL_UART_Transmit_IT(&huart4,&TX_DATA[0],1);
}
RX_DATA[0] = 0;
}
}
task_count[COM_TO_Andr] = 0;
}
}
FPGA通信部分:用到了三级状态机
int i;
uint8_t ongoing1,ongoing2,ongoing3,ongoing4;
void fpga_com_task(uint32_t period)
{
if(task_count[COM_TO_FPGA] >= period){
if(sevo_cmd_type == GARBAGE_1 || ongoing1 ==1)
{
if(sevo_step == 1)
{
if(sevo_cmd == 1){
sevo_cmd = 0;
ongoing1 = 1;
for(i=0;i<5000;i++)
{
TX_DATA_TO_FPGA = 21;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
TX_DATA_TO_FPGA = 15;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
for(i=0;i<30000;i++);
sevo_step = 2;
}
}
else if(sevo_step == 2)
{
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 134;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 144;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
sevo_step = 3;
}
else if(sevo_step == 3)
{
for(i=0;i<5000;i++)
{
TX_DATA_TO_FPGA = 9;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
for(i=0;i<3000;i++)
{
TX_DATA_TO_FPGA = 15;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
garbage_complete = 1;
}
ongoing1 = 0;
sevo_step =1;
}
}
else if(sevo_cmd_type == GARBAGE_2|| ongoing2 ==1)
{
if(sevo_step == 1)
{
if(sevo_cmd == 1){
sevo_cmd = 0;
ongoing2 = 1;
for(i=0;i<9300;i++)
{
TX_DATA_TO_FPGA = 23;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
TX_DATA_TO_FPGA = 15;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
sevo_step = 2;
}
}
else if(sevo_step == 2)
{
for(i=0;i<8000;i++)
{
TX_DATA_TO_FPGA = 122;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
for(i=0;i<8000;i++)
{
TX_DATA_TO_FPGA = 144;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
sevo_step = 3;
}
else if(sevo_step == 3)
{
for(i=0;i<9300;i++)
{
TX_DATA_TO_FPGA = 7;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
sevo_step =1;
TX_DATA_TO_FPGA = 15;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
garbage_complete = 1;
ongoing2 = 0;
}
}
else if(sevo_cmd_type == GARBAGE_3|| ongoing3 ==1)
{
if(sevo_step == 1)
{
if(sevo_cmd == 1){
sevo_cmd = 0;
ongoing3 = 1;
for(i=0;i<5000;i++)
{
TX_DATA_TO_FPGA = 9;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
TX_DATA_TO_FPGA = 15;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
sevo_step = 2;
}
}
else if(sevo_step == 2)
{
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 126;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0X5);
}
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 144;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0X5);
}
sevo_step = 3;
}
else if(sevo_step == 3)
{
for(i=0;i<5000;i++)
{
TX_DATA_TO_FPGA = 21;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
TX_DATA_TO_FPGA = 15;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
garbage_complete = 1;
sevo_step =1;
ongoing3 = 0;
}
}
else if(sevo_cmd_type == GARBAGE_4|| ongoing4 ==1)
{
if(sevo_step == 1)
{
if(sevo_cmd == 1){
sevo_cmd = 0;
ongoing4 = 1;
for(i=0;i<9000;i++)
{
TX_DATA_TO_FPGA = 9;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
for(i=0;i<2000;i++)
{
TX_DATA_TO_FPGA = 15;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
sevo_step = 2;
}
}
else if(sevo_step == 2)
{
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 126;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
for(i=0;i<6000;i++)
{
TX_DATA_TO_FPGA = 144;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
sevo_step = 3;
}
else if(sevo_step == 3)
{
for(i=0;i<8700;i++)
{
TX_DATA_TO_FPGA = 21;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
for(i=0;i<2000;i++)
{
TX_DATA_TO_FPGA = 15;
HAL_UART_Transmit(&huart3,&TX_DATA_TO_FPGA,1,0XF);
}
garbage_complete = 1;
sevo_step =1;
ongoing4 = 0;
}
}
task_count[COM_TO_FPGA] = 0;
}
}