这个项目的最后一篇博客也是拖了很久了,从开始复习那会儿拖到了期末考完回了家,今天就赶紧把它了结了,让这个项目正式收官。
STM32部分可以分为按键控制、LCD2004显示、舵机控制、数据的存储、与树莓派的通信、RTC时钟这六个小的模块。
在本系统中我使用的是4*4矩阵键盘,8个引脚,行列分别扫描来获取按下的键值,驱动是很简单的就不赘述了,我对于按键控制的解决办法是使用信号量以及完全等待输入两种做法,在诸如模式选择、按下确认键、按下返回刷屏等功能的实现上按键按下会发布信号量以此来进行任务的同步;而在密码输入部分,是使用完全等待的办法,这里系统不会进行打断,除非密码超出限长、按下确认或退出键。
mode=KEY_Scan(0); //按键扫描
if(mode!=0)
{
switch(mode)
{
case KEYA_PRESS:
//printf("Mode A\r\n");
OSSemPost(&MODE_A_SEM,OS_OPT_POST_1,&err);//发送模式A信号量
break;
case KEYB_PRESS:
//printf("Mode B\r\n");
OSSemPost(&MODE_B_SEM,OS_OPT_POST_1,&err);//发送模式B信号量
break;
case KEYC_PRESS:
//printf("Mode C\r\n");
OSSemPost(&MODE_C_SEM,OS_OPT_POST_1,&err);//发送模式C信号量
break;
case KEYD_PRESS:
//printf("Mode D\r\n");
OSSemPost(&MODE_D_SEM,OS_OPT_POST_1,&err);//发送模式D信号量
break;
default:
break;
}
}
我使用的LCD2004是串行接口,IIC驱动,我使用了32的硬件IIC,使用还是很简单的,驱动提供了显示字符串以及数字的函数。
LCDI2C_clear();
LCDI2C_Show_String(5,0,"SMATA DOOR");
LCDI2C_Show_Num(1,1,4,calendar.w_year);
LCDI2C_Show_Num(6,1,2,calendar.w_month);
LCDI2C_Show_Num(9,1,2,calendar.w_date);
LCDI2C_Show_Num(13,1,2,calendar.hour);
LCDI2C_Show_Num(16,1,2,calendar.min);
LCDI2C_Show_String(5,1,"/");
LCDI2C_Show_String(8,1,"/");
LCDI2C_Show_String(15,1,":");
LCDI2C_Show_String(2,2,"A:Face");
LCDI2C_Show_String(12,2,"B:Pwd");
LCDI2C_Show_String(2,3,"C:Visit");
LCDI2C_Show_String(12,3,"D:Root");
出于实时性的考虑,我将PID的计算放在了树莓派上,树莓派通过串口发回给32的是PWM的控制量,两路PWM通过改变占空比来控制舵机的转向。
其实这个才是我感觉这个项目中最为有趣的一个部分,我本来是打算使用SPI FLASH或者EEPROM来存储开门信息以及ID和密码信息,但是看到下载完所有的代码才使用了40K+的FLASH容量,所以我使用片上FLASH的后半部分来存储信息,F103C8T6手册上写的是64K的FLASH容量,但是由于一些技术原因(我在一篇博客中有讲述*地址*)可用空间其实可以很大,所以说存放信息是绰绰有余的,因为我存放的只有开门人ID(U8类型)、开门方式(U8类型)、开门时间(时分秒,3*U8类型)、开门日期(年月日,3*u8类型),一次开门仅有8个字节的数据。
关于密码和开门记录我使用的方法都是是在一个靠前的位置存放保存数据的数量,在一个靠后的位置开始存放数据。这样操作的优点是我们可以先读出数量然后再去根据数量去读数据,如果想要将数据清零没必要擦除扇区,只要将数量值写为0即可。
//设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)
#define FLASH_PWD_NUM_ADDR 0X0800B000 //存储密码及ID个数
#define FLASH_PWD_ADDR 0X0800B100 //存储ID及其对应的密码
#define FLASH_SAVE_ADDR 0X08010000 //存储数据个数
#define FLASH_DATE_SAVE_ADDR 0X08010100 //数据存储起始地址
/****************************************************************************************/
/*开门记录函数 */
/*参数:要存储的数据数组 */
/*返回值:无 */
/*作者:高宇玥 */
/* 修改记录:创建 2018.12.10 */
/* */
/****************************************************************************************/
void Data_storage(u8 data_to_storage[8])
{
u16 date_num;
date_num=STMFLASH_ReadHalfWord(FLASH_SAVE_ADDR);//读出当前的开门记录数量
STMFLASH_Write(FLASH_DATE_SAVE_ADDR+date_num*8,(u16 *)data_to_storage,DATE_SIZE);//将本次记录写入
date_num++;//数量值自增
STMFLASH_Write(FLASH_SAVE_ADDR,&date_num,1);//写数量值写入
}
这部分就不赘述了,以前专门写过一篇如何使用Python的pack方法将数据打包发送的,32端的解析也讲的比较清楚了,大家可以去看看我那一篇博客*地址*。
我使用的这个是C8T6的最小系统板,真的是十分精简了,没有给备份区供电的纽扣电池,但是它焊上了32.768KHz的晶振,美滋滋,我只接了块电池,写了下驱动就搞定了RTC时钟部分。
配置代码就不说了,使用的是原子的例程中的代码,系统读取时间我使用的方法是使用软件定时器,100ms读一次,定时器的回调函数如下:
/*************************************************************************************/
/*RTC软件定时器回调函数 */
/*参数:无 */
/*返回值:无 */
/*作者:高宇玥 */
/* 修改记录:添加部分注释 2018.12.7 */
/* */
/********************************************************************************** */
//定时器1回调函数
void tmr2_callback_RTC(void *p_tmr, void *p_arg)
{
//显示
LCDI2C_Show_Num(1,1,4,calendar.w_year);
LCDI2C_Show_Num(6,1,2,calendar.w_month);
LCDI2C_Show_Num(9,1,2,calendar.w_date);
LCDI2C_Show_Num(13,1,2,calendar.hour);
LCDI2C_Show_Num(16,1,2,calendar.min);
LCDI2C_Show_String(5,1,"/");
LCDI2C_Show_String(8,1,"/");
LCDI2C_Show_String(15,1,":");
}
这个项目做得比较仓促但还是收获很多的,这是我第一个使用了Linux的项目,也是第一个使用UCOS的较大的项目,做的过程中确实遇到了不少的问题,不少的难关,但最后还是努力克服了,当然系统远远没有做到完善,无论是显示的美观程度还是程序的鲁棒性还是人脸识别的精确度其实都还有很多的提升空间。其实我认为有的项目需要去做精做细,而有的项目其实只是为了搭建一个原型系统去练习一些技术,比如这个,因为这个项目从一开始的规划其实就是一个学习的练手项目,我以次来练习OpenCV练习UCOS,以后如果完善的话我会完全使用树莓派或者Cortex-A系列开发板完成,全部使用Linux系统,而不是RTOS+Linux中奇葩的组合-.-(在此挖坑了)