前言:
不久前结束了硬件课设,想着也花了不少精力做这个,走了不少弯路,找了网上很多资料,但无奈都是单独的例程文件,自己不断整合调整后才得到了现在这个版本,故在此写个博客记录一下。本项目主要是基于stm32f103c8t6进行的智能门锁设计,实现了键盘解锁、指纹解锁、RFID解锁,解锁过程主要用步进电机进行模拟。
一、材料准备
1、bom表
注意其中的USB转TTL可以不买,视读者自身调试代码能力而定。
另外还需要一些其他材料,比如杜邦线、面包板,各种芯片或者小原件的排针需要焊接的最好让商家焊好,不然之后调试过程就会贼痛苦(真的是血的教训)。当然,焊接能力到位的读者还可以尝试买洞洞板和导线让自己的作品更美观。
二、模块设计
1、OLED模块
在将芯片安装在面包板上后,我们可以先安装OLED模块以便之后其他模块的调试。因为我们买的是4针的OLED屏,代码编写的时候也要注意用对应的iic协议。
部分代码如下:
#include "oled_iic.h"
#include "Delay.h"
#include "oledfont.h"
#include "string.h"
unsigned int HZ=0;
//返回GB16字库里汉字个数
unsigned int GB16_NUM(void)
{
unsigned int HZ_NUM;
unsigned char *PT;
PT = hz_index;
while(*PT++ != '\0')
{
HZ_NUM++;
}
return HZ_NUM/2;
}
void IIC_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(OLED_SCL_RCC|OLED_SDA_RCC, ENABLE); /* 打开GPIO时钟 */
GPIO_InitStructure.GPIO_Pin = OLED_SCL_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出 */
GPIO_Init(OLED_SCL_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = OLED_SDA_PIN;
GPIO_Init(OLED_SDA_GPIO_PORT, &GPIO_InitStructure);
/* 给一个停止信号, 复位IIC总线上的所有设备到待机模式 */
IIC_Stop();
}
static void IIC_Delay(void)
{
uint8_t i;
/*
下面的时间是通过逻辑分析仪测试得到的。
工作条件:CPU主频72MHz ,MDK编译环境,1级优化
循环次数为10时,SCL频率 = 205KHz
循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
*/
for (i = 0; i < 10; i++);
}
/*
*********************************************************************************************************
* 函 数 名: IIC_Start
* 功能说明: CPU发起IIC总线启动信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void IIC_Start(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示IIC总线启动信号 */
OLED_SDA=1;
OLED_SCL=1;
IIC_Delay();
OLED_SDA=0;
IIC_Delay();
OLED_SCL=0;
IIC_Delay();
}
其实OLED的代码网上都可以找得到,根据自己的要求随便改改就行。
2、矩阵按键
对于按键来说,4x4的规格既能满足要求,又有较强的可拓展性,属于新手首选。至于运行原理,则是采用的最常用的逐行逐列扫描法。
部分代码如下:
#include "KEY1.h"
#include "delay.h"
#include "sys.h"
#include "usart3.h"
#include <stdio.h>
unsigned char Y1,Y2,Y3,Y4;
extern uchar KeyVal;
extern uint8_t MEAU;
void Matrix_Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(X1_RCC|X2_RCC|X3_RCC|X4_RCC|Y1_RCC|Y2_RCC|Y3_RCC|Y4_RCC, ENABLE);
GPIO_InitStructure.GPIO_Pin = X1_GPIO_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(X1_GPIO_PORT, &GPIO_InitStructure); //C13
GPIO_InitStructure.GPIO_Pin = X2_GPIO_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(X2_GPIO_PORT, &GPIO_InitStructure); //C15
GPIO_InitStructure.GPIO_Pin = X3_GPIO_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(X3_GPIO_PORT, &GPIO_InitStructure); //B1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = X4_GPIO_PIN ;
GPIO_Init(X4_GPIO_PORT, &GPIO_InitStructure); //B11
GPIO_InitStructure.GPIO_Pin = Y1_GPIO_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(Y1_GPIO_PORT, &GPIO_InitStructure); //B15
GPIO_InitStructure.GPIO_Pin = Y2_GPIO_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(Y2_GPIO_PORT, &GPIO_InitStructure); //B14
GPIO_InitStructure.GPIO_Pin = Y3_GPIO_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(Y3_GPIO_PORT, &GPIO_InitStructure); //B13
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = Y4_GPIO_PIN;
GPIO_Init(Y4_GPIO_PORT, &GPIO_InitStructure); //B12
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 ;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_Init(GPIOF, &GPIO_InitStructure);
}
3、AS608指纹模块
指纹模块比较容易出问题,所以一般找网上他人已经跑通的例程比较保险,看懂代码后根据自己的需求微调即可。
部分代码如下:
#include <string.h>
#include <stdio.h>
#include "delay.h"
#include "usart3.h"
#include "as608.h"
#include "oled_iic.h"
#include "key1.h"
#include "LED.h"
#include "SERVO.h"
#include "walkmotor.h"
u32 AS608Addr = 0XFFFFFFFF; //默认 0016 0016 0016 0016 0016 0016 0016 0016
char str2[6] = {0};
//串口发送一个字节
static void MYUSART_SendData(u8 data)
{
while((USART2->SR & 0X40) == 0);
USART2->DR = data;
}
//发送包头
static void SendHead(void)
{
MYUSART_SendData(0xEF);
MYUSART_SendData(0x01);
}
//发送地址
static void SendAddr(void)
{
MYUSART_SendData(AS608Addr >> 24);
MYUSART_SendData(AS608Addr >> 16);
MYUSART_SendData(AS608Addr >> 8);
MYUSART_SendData(AS608Addr);
}
//发送包标识,
static void SendFlag(u8 flag)
{
MYUSART_SendData(flag);
}
4、RFID模块
RFID解锁是通过读取射频卡片的扇区内容进行识别的解锁方式,具有方便、安全的特点,用读卡器读取到卡片UID后,还可以实现校园卡解锁,看起来还是很高级的。
部分代码:
#include "rc522.h"
#include "Delay.h"
#include "Serial.h"
#include "string.h"
#include "LED.h"
//
// M1卡分为16个扇区,每个扇区由四个块(块0、块1、块2、块3)组成
// 将16个扇区的64个块按绝对地址编号为:0~63
// 第0个扇区的块0(即绝对地址0块),用于存放厂商代码,已经固化不可更改
// 每个扇区的块0、块1、块2为数据块,可用于存放数据
// 每个扇区的块3为控制块(绝对地址为:块3、块7、块11.....)包括密码A,存取控制、密码B等
/*******************************
*连线说明:
*1--SDA <----->PA4
*2--SCK <----->PA5
*3--MOSI <----->PA7
*4--MISO <----->PA6
*5--悬空
*6--GND <----->GND
*7--RST <----->PB0
*8--VCC <----->VCC
************************************/
/*全局变量*/
unsigned char CT[2];//卡类型
unsigned char SN[4]; //卡号
unsigned char DATA[16]; //存放数据
unsigned char RFID[16]; //存放RFID
unsigned char card0_bit=0;
unsigned char card1_bit=0;
unsigned char card2_bit=0;
unsigned char card3_bit=0;
unsigned char card4_bit=0;
unsigned char total=0;
// 替换成自己卡的UID
unsigned char card_0[4]= {73,224,5,152};
unsigned char card_1[4]= {105,102,100,152};
unsigned char card_2[4]= {208,121,31,57};
unsigned char card_3[4]= {176,177,143,165};
unsigned char card_4[4]= {5,158,10,136};
u8 KEY_A[6]= {0xff,0xff,0xff,0xff,0xff,0xff};
u8 KEY_B[6]= {0xff,0xff,0xff,0xff,0xff,0xff};
u8 AUDIO_OPEN[6] = {0xAA, 0x07, 0x02, 0x00, 0x09, 0xBC};
// 测试用 3区块数据
unsigned char RFID1[16]= {0x10,0x20,0x30,0x40,0x50,0x60,0xff,0x07,0x80,0x29,0x01,0x02,0x03,0x04,0x05,0x06};
unsigned char RFID2[16]= {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x80,0x29,0xff,0xff,0xff,0xff,0xff,0xff};
// 测试用 3区块密钥
u8 KEY_A1[6]= {0x10,0x20,0x30,0x40,0x50,0x60};
u8 KEY_A2[6]= {0x00,0x00,0x00,0x00,0x00,0x00};
u8 KEY_B1[6]= {0x01,0x02,0x03,0x04,0x05,0x06};
u8 KEY_B2[6]= {0x10,0x20,0x30,0x00,0x00,0x00};
u8 KEY_B3[6]= {0x01,0x02,0x03,0x00,0x00,0x00};
// 置零用
unsigned char DATA0[16]= {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
unsigned char DATA1[16]= {0x12,0x34,0x56,0x78,0x9A,0x00,0xff,0x07,0x80,0x29,0xff,0xff,0xff,0xff,0xff,0xff};
unsigned char status;
5、步进电机
实现各种解锁方式后,还需要一个外设模拟开关门动作,这里我们用步进电机的转动实现。其实有条件的读者可以3d打印一些材料,进行拼接后让步进电机带动塑料门,真正模拟出开关门的操作。
部分代码:
#include "stm32f10x.h" // Device header
#include "walkmotor.h"
#include "Delay.h"
/********************************
A B C D
D3 D5 D7 XCLK
PC9 PB6 PE6 PA8
********************************/
unsigned short zheng[4] = {0x0800,0x0400,0x0200,0x0100};//D-C-B-A 正转
unsigned short fan[4] = {0x0100,0x0200,0x0400,0x0800};//A-B-C-D 反转
unsigned short Init[4] = {0x0,0x0,0x0,0x0};
void Walkmotor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能GPIOA时钟
//GPIO初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9| GPIO_Pin_10| GPIO_Pin_11| GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
GPIO_ResetBits(GPIOA, GPIO_Pin_9| GPIO_Pin_10 | GPIO_Pin_11| GPIO_Pin_12);
}
void StatusA(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_9);
GPIO_ResetBits(GPIOA, GPIO_Pin_10| GPIO_Pin_11 | GPIO_Pin_12);
}
void StatusB(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_9);
GPIO_SetBits(GPIOA, GPIO_Pin_10);
GPIO_ResetBits(GPIOA, GPIO_Pin_11| GPIO_Pin_12);
}
三、成果展示
智能门锁演示视频
四、项目总结
本次硬件课设极大地增强了我对单片机的认识,代码能力蹭蹭地往上涨,大有收获。当然,需要项目源文件的读者可以点击下方链接进行打赏,截图发我邮箱后就可以啦,最后祝大家都能顺利通过课设答辩,向着硬件工程师的目标不断迈进!