基于STM32F103宿舍指纹锁
前言
参考文章
源码
准备工作
注
:链接仅供参考,可自行准备,很多其实没必要买可以画PCB顺便画上去(主要我不会,只能买现成的模块)
硬件 | 链接 | 价格 |
---|---|---|
STM32F103C8T6最小系统板 | 拼夕夕 | 15.6r |
ST003 三位按键模块 | 拼夕夕 | 9r |
有源蜂鸣器模块(低电平触发) | 拼夕夕 | 3.2r |
1.3寸OLED显示屏12864–带中文字库 | 淘宝 | 24r |
AS608光学指纹模块 | 淘宝 | 44.8r |
SG90舵机 | 拼夕夕 | 6r |
/ | / | 合计102.6r |
- 立创画底板,用于插最小系统板还有其他模块,打板的话是立创每个月有免费次数的10*10以内好话免费(下单才知道我舵机插座忘了画)
- 连接图
待写
- 下载程序
下载的话有两种方案:
- USB转串口模块
USB转串口 | STM32管脚 |
---|---|
3V3 | 3V3 |
GND | GND |
TXD | PA10(USART1_RX) |
RXD | PA9(USART1_TX) |
然后使用 FlyMcu软件进行下载,需要设置:
下程序时跳线帽需要这样设置:
下载完后需要拔其中一个否则程序不会运行
- 使用DAPLink
DAPLINK模块 | STM32管脚 |
---|---|
3V3 | 3V3 |
GND | GND |
SWD | SWD |
SWCK | SCK |
程序框架
流程图制作可参考网站:ioDraw
大致流程如下:
MX配置
程序编写
LED
- 硬件连接(最小系统板自带的LED)
LED | STM32管脚 |
---|---|
LD1 | PC13 |
- 程序编写
Led.h
/*
*@Description:
*@Author: Yang
*@Date: 2023-04-21 15:16:50
*/
#ifndef __LED_H
#define __LED_H
#include "MyAll.h"
// LED引脚
#define LED_PIN GPIO_PIN_13
// LED灭
#define LED_OFF 0
// LED亮
#define LED_ON 1
typedef struct
{
void (*vLed_Control)(uint8_t);
void (*vLed_Flashing)(void);
}Led_TypeDef;
extern Led_TypeDef Led_Data;
void vLed_Control(uint8_t swch); //控制LED亮灭
void vLed_Flashing(void); //LED闪烁
#endif
Led.c
/*
*@Description: LED
*@Author: Yang
*@Date: 2023-04-21 15:16:57
*/
#include "Led.h"
/*====================================变量区 BEGIN====================================*/
Led_TypeDef Led_Data =
{
.vLed_Control = &vLed_Control,
.vLed_Flashing = &vLed_Flashing
};
/*====================================变量区 END====================================*/
/*
* @description: 控制LED亮灭
* @param: LED_OFF--灭 LED_ON--亮
* @return: 无
* @Date: 2023-04-21 16:51:41
*/
// 控制LED亮灭
void vLed_Control(uint8_t swch)
{
if (LED_ON == swch)
{
HAL_GPIO_WritePin(GPIOC, LED_PIN, GPIO_PIN_RESET);
}
else if (LED_OFF == swch)
{
HAL_GPIO_WritePin(GPIOC, LED_PIN, GPIO_PIN_SET);
}
}
/*
* @description: LED闪烁
* @return: 无
* @Date: 2023-04-22 00:16:47
*/
// LED闪烁
void vLed_Flashing(void)
{
static uint8_t Led_State = LED_OFF;
Led_State = !Led_State;
Led_Data.vLed_Control(Led_State);
}
按键
- 硬件连接
按键 | STM32管脚 |
---|---|
KEY1 | PB12 |
KEY2 | PB13 |
KEY3 | PB14 |
- 程序编写
Key.h
/*
*@Description: 按键
*@Author: Yang
*@Date: 2023-04-21 15:16:44
*/
#ifndef __KEY_H
#define __KEY_H
#include "MyAll.h"
// 按键1引脚PB12
#define KEY1_PIN GPIO_PIN_12
// 按键2引脚PB13
#define KEY2_PIN GPIO_PIN_13
// 按键3引脚PB14
#define KEY3_PIN GPIO_PIN_14
// 读取按键1电平状态
#define KEY1 HAL_GPIO_ReadPin(GPIOB,KEY1_PIN)
// 读取按键2电平状态
#define KEY2 HAL_GPIO_ReadPin(GPIOB,KEY2_PIN)
// 读取按键3电平状态
#define KEY3 HAL_GPIO_ReadPin(GPIOB,KEY3_PIN)
// 无按键
#define KEY_NULL 0
// 按键1
#define KEY1_VALUE 1
// 按键2
#define KEY2_VALUE 2
// 按键3
#define KEY3_VALUE 3
typedef struct
{
// 标志是否处于长按状态
bool Key_Is_Long_Press_Flag;
// 存储按键状态(3短3长)
uint8_t Key_Down_Buff[6];
// 按键按下一瞬间
uint8_t Key_Down;
// 按键抬起一瞬间
uint8_t Key_Up;
// 按键键值
uint8_t Key_Value;
// 按键按下时间
uint16_t Key_Down_Time;
uint8_t (*ucKey_Get_Value)(void);
void (*vKey_Scan_Function)(void);
void (*vKey_Run_Function)(void);
void (*vKey_Flag_Init)(void);
}Key_TypeDef;
extern Key_TypeDef Key_Data;
uint8_t ucKey_Get_Value(void); // 获取按键键值函数
void vKey_Scan_Function(void); // 按键扫描函数
void vKey_Run_Function(void); // 按键功能执行函数
void vKey_Flag_Init(void);
#endif
Key.c
/*
*@Description: 按键
*@Author: Yang
*@Date: 2023-04-21 15:16:38
*/
#include "Key.h"
/*====================================变量区 BEGIN====================================*/
Key_TypeDef Key_Data =
{
.Key_Is_Long_Press_Flag = 0,
.Key_Down_Buff = {0},
.Key_Down = 0,
.Key_Up = 0,
.Key_Value = 0,
.Key_Down_Time = 0,
.ucKey_Get_Value = &ucKey_Get_Value,
.vKey_Scan_Function = &vKey_Scan_Function,
.vKey_Run_Function = &vKey_Run_Function,
.vKey_Flag_Init = &vKey_Flag_Init
};
/*====================================变量区 END====================================*/
/*
* @description: 获取键值
* @return: 键值--0/1/2/3
* @Date: 2023-04-21 17:30:19
*/
// 获取键值
uint8_t ucKey_Get_Value(void)
{
if((!KEY1) || (!KEY2) || (!KEY3))
{
if(!KEY1)
{
return KEY1_VALUE;
}
else if(!KEY2)
{
return KEY2_VALUE;
}
else if(!KEY3)
{
return KEY3_VALUE;
}
}
return KEY_NULL;
}
/*
* @description: 按键扫描函数
* @return: 无
* @Date: 2023-04-21 17:34:56
*/
// 按键扫描函数
void vKey_Scan_Function(void)
{
static uint8_t Key_Old_Value = 0;
Key_Data.Key_Value = Key_Data.ucKey_Get_Value();
Key_Data.Key_Up = ~Key_Data.Key_Value & (Key_Data.Key_Value^Key_Old_Value);
Key_Data.Key_Down = Key_Data.Key_Value & (Key_Data.Key_Value^Key_Old_Value);
Key_Old_Value = Key_Data.Key_Value;
if(Key_Data.Key_Down)
{
Key_Data.Key_Down_Time = 0; //按键计数清0开始计数
Key_Data.Key_Is_Long_Press_Flag = 0;
}
// 短按---<1s
if(Key_Data.Key_Down_Time < 10)
{
switch(Key_Data.Key_Up)
{
case KEY1_VALUE:
{
Key_Data.Key_Down_Buff[0] = 1;
break;
}
case KEY2_VALUE:
{
Key_Data.Key_Down_Buff[1] = 1;
break;
}
case KEY3_VALUE:
{
Key_Data.Key_Down_Buff[2] = 1;
break;
}
default:break;
}
Key_Data.Key_Is_Long_Press_Flag = 0;
}
// 长按
else
{
if (0 == Key_Data.Key_Is_Long_Press_Flag)
{
switch (Key_Data.Key_Value)
{
case KEY1_VALUE:
{
Key_Data.Key_Down_Buff[3] = 1;
break;
}
case KEY2_VALUE:
{
Key_Data.Key_Down_Buff[4] = 1;
break;
}
case KEY3_VALUE:
{
Key_Data.Key_Down_Buff[5] = 1;
break;
}
default:
break;
}
Key_Data.Key_Is_Long_Press_Flag = 1;
}
}
}
/*
* @description: 按键功能执行函数
* @return: 无
* @Date: 2023-04-21 18:14:29
*/
// 按键功能执行函数
void vKey_Run_Function(void)
{
static float x = 0;
if(Key_Data.Key_Down_Buff[0])
{
Key_Data.Key_Down_Buff[0] = 0;
SG90_Data.vSG90_Set_Duty(SG90_ANGLE_45);
}
if(Key_Data.Key_Down_Buff[1])
{
Key_Data.Key_Down_Buff[1] = 0;
SG90_Data.vSG90_Set_Duty(SG90_ANGLE_90);
}
if(Key_Data.Key_Down_Buff[2])
{
Key_Data.Key_Down_Buff[2] = 0;
SG90_Data.vSG90_Set_Duty(SG90_ANGLE_180);
}
if(Key_Data.Key_Down_Buff[3])
{
Key_Data.Key_Down_Buff[3] = 0;
SG90_Data.vSG90_Set_Duty(SG90_ANGLE_0);
}
}
/*
* @description: 按键按下标志位清0
* @return {*}
* @Date: 2023-04-24 23:30:03
*/
// 按键按下标志位清0
void vKey_Flag_Init(void)
{
for(uint8_t i = 0; i < 6; i++)
{
Key_Data.Key_Down_Buff[i] = 0;
}
}
蜂鸣器
- 硬件连接
蜂鸣器 | STM32管脚 |
---|---|
I/O | PB15 |
- 程序编写
Buzzer.h
/*
*@Description: 蜂鸣器
*@Author: Yang
*@Date: 2023-04-21 15:16:31
*/
#ifndef __BUZZER_H
#define __BUZZER_H
#include "MyAll.h"
// 蜂鸣器引脚PB15
#define BUZZER_PIN GPIO_PIN_15
// 蜂鸣器响
#define BUZZER_ON 1
// 蜂鸣器不响
#define BUZZER_OFF 0
// 蜂鸣器响的时间(ms)
#define BUZZER_TIME 120
typedef struct
{
// 蜂鸣器开始标志位
bool Buzzer_Open_Flag;
uint8_t Buzzer_State; //蜂鸣器当前状态
void (*vBuzzer_Control)(uint8_t);
void (*vBuzzer_Ring)(void);
}Buzzer_TypeDef;
extern Buzzer_TypeDef Buzzer_Data;
void vBuzzer_Control(uint8_t swch);
void vBuzzer_Ring(void);
#endif
Buzzer.c
/*
*@Description: 蜂鸣器
*@Author: Yang
*@Date: 2023-04-21 15:16:25
*/
#include "Buzzer.h"
/*====================================变量区 BEGIN====================================*/
Buzzer_TypeDef Buzzer_Data =
{
.Buzzer_Open_Flag = 0,
.Buzzer_State = BUZZER_OFF,
.vBuzzer_Control = &vBuzzer_Control,
.vBuzzer_Ring = &vBuzzer_Ring
};
/*====================================变量区 END====================================*/
/*
* @description: 蜂鸣器控制
* @param: BUZZER_ON--响 BUZZER_OFF--不响
* @return: 无
* @Date: 2023-04-21 18:28:41
*/
// 蜂鸣器控制
void vBuzzer_Control(uint8_t swch)
{
if(BUZZER_ON == swch)
{
HAL_GPIO_WritePin(GPIOB,BUZZER_PIN,GPIO_PIN_RESET);
Buzzer_Data.Buzzer_State = BUZZER_ON;
}
else if(BUZZER_OFF == swch)
{
HAL_GPIO_WritePin(GPIOB,BUZZER_PIN,GPIO_PIN_SET);
Buzzer_Data.Buzzer_State = BUZZER_OFF;
}
}
/*
* @description: 蜂鸣器响一段时间
* @return: 无
* @Date: 2023-04-23 20:07:54
*/
// 蜂鸣器响一段时间
void vBuzzer_Ring(void)
{
Buzzer_Data.Buzzer_Open_Flag = 1;
}
OLED
- 接线(看模块丝印,正负极不要接反!),因为我想把串口2的发送接收引脚空出来所以PA2,PA3改成PA6,PA7
OLED | STM32管脚 |
---|---|
3V3 | 3V3 |
GND | GND |
CLK(SCL) | PA0 |
MOS(SDA) | PA1 |
DC | PA6 |
CS1* | PA7 |
FS0 | PA4 |
CS2* | PA5 |
- 然后把商家的例程烧进去看看是否正常亮,注意引脚跟程序的引脚是否一致
OLED屏幕像素是
128*64(宽128高64)
显示两个字
16*16
的话左下角是x:0 y:6
,右下角是x:96 y:6
(因为96+16+16=128)
16*16
的话高是0~6
,宽是0~128
,最多一行显示8个中文
还有太空人的图片.c在
Space_Person_Bmp
文件夹就不展示了太长了
滚动的话可以这样:
需要在字体后面加点空格不然可能有乱码,它是整个屏幕移动的
Oled_Data.vOled_Clear(); Oled_Data.vOled_Write_Byte(0x2E, OLED_CMD); //关闭滚动 Oled_Data.vOled_Write_Byte(0x29, OLED_CMD); //向右滚动,27则向左 Oled_Data.vOled_Write_Byte(0x00, OLED_CMD); //虚拟字节 Oled_Data.vOled_Write_Byte(0x00, OLED_CMD); //起始页 这里为0 Oled_Data.vOled_Write_Byte(0x07, OLED_CMD); //滚动速度 Oled_Data.vOled_Write_Byte(0x01, OLED_CMD); //终止页 Oled_Data.vOled_Write_Byte(0x00, OLED_CMD); //虚拟字节 Oled_Data.vOled_Write_Byte(0xFF, OLED_CMD); //虚拟字节 Oled_Data.vOled_Display_Gb2312_String(30,0,(uint8_t*)"智能嵌入式技 "); Oled_Data.vOled_Display_Gb2312_String(30,3,(uint8_t*)"能大师工作室 "); Oled_Data.vOled_Display_Gb2312_String(55,6,(uint8_t*)"欢迎您 "); Oled_Data.vOled_Write_Byte(0x2F, OLED_CMD); //开启滚动
- 程序编写
Clock.h
/*
*@Description:
*@Author: Yang
*@Date: 2023-04-25 14:20:30
*/
#ifndef __CLOCK_H
#define __CLOCK_H
#include "MyAll.h"
// 时间选择索引
// 时
#define CLOCK_HOUR 1
// 分
#define CLOCK_MIN 2
// 秒
#define CLOCK_SEC 3
//时间结构体
typedef struct
{
uint8_t hour;
uint8_t min;
uint8_t sec;
//公历日月年周
uint16_t w_year;
uint8_t w_month;
uint8_t w_date;
uint8_t week;
void (*vClock_Symbol)(uint8_t, uint8_t);
void (*vClock_Display_Function)(void);
void (*vClock_Get_Time_Date)(RTC_TimeTypeDef *);
void (*vClock_Set_Time)(uint8_t, uint8_t, uint8_t);
} Clock_TypeDef;
extern Clock_TypeDef Clock_Data;
extern uint8_t Clock_Arr[10];
void vClock_Symbol(uint8_t x, uint8_t y);
void vClock_Display_Function(void);
void vClock_Get_Time_Date(RTC_TimeTypeDef *sTime);
void vClock_Set_Time(uint8_t h, uint8_t m, uint8_t s);
#endif
Clock.c
#include "Clock.h"
/*====================================变量区 BEGIN====================================*/
// 字库数组
extern const uint8_t F11x16[][22];
extern const uint8_t F19x24[][57];
uint8_t Clock_Arr[10];
// 秒数组
uint16_t num_buf[4][11];
// 分数组
uint32_t num1_buf[4][19];
// 时数组
uint32_t num2_buf[4][19];
extern RTC_TimeTypeDef rtc_Time;
extern RTC_DateTypeDef rtc_Date;
Clock_TypeDef Clock_Data =
{
.0,
.0,
.0,
.0,
.0,
.0,
.0,
.vClock_Symbol = &vClock_Symbol,
.vClock_Display_Function = &vClock_Display_Function,
.vClock_Get_Time_Date = &vClock_Get_Time_Date,
.vClock_Set_Time = &vClock_Set_Time
};
/*====================================变量区 END====================================*/
/*
* @description: 画时钟符号 ":"
* @param {uint8_t} x 范围0~128
* @param {uint8_t} y 范围0~7
* @return {*} 无
* @Date: 2023-04-25 14:40:12
*/
// 画时钟符号 ":"
void vClock_Symbol(uint8_t x, uint8_t y)
{
uint8_t i = 0;
uint8_t Point_Str[] =
{
0x00, 0xF8, 0xF8, 0xF8, 0xF8, 0x00,
0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0x00,
0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x00
};
Oled_Data.vOled_Set_Address(x, y); // 设置坐标
for (i = 0; i < 6; i++)
{
Oled_Data.vOled_Write_Byte(Point_Str[i], 1);
}
Oled_Data.vOled_Set_Address(x, y + 1); // 设置坐标
for (i = 6; i < 12; i++)
{
Oled_Data.vOled_Write_Byte(Point_Str[i], 1);
}
Oled_Data.vOled_Set_Address(x, y + 2); // 设置坐标
for (i = 12; i < 18; i++)
{
Oled_Data.vOled_Write_Byte(Point_Str[i], 1);
}
}
/*
* @description: 时钟显示
* @return {*}
* @Date: 2023-04-25 14:56:45
*/
// 时钟显示
void vClock_Display_Function(void)
{
SearchResult search;
// 保存的秒数
static uint8_t Save_Time = 0;
uint8_t i = 0, x = 0, y = 0;
// 清屏
Oled_Data.vOled_Clear();
// 显示字体
Oled_Data.vOled_Display_Gb2312_String(0, 6, (uint8_t *)"解锁");
Oled_Data.vOled_Display_Gb2312_String(96, 6, (uint8_t *)"功能");
sprintf(Clock_Arr, "%02d", Clock_Data.hour);
Oled_Data.vOled_Asc_19x24(2, 2, Clock_Arr);
Clock_Data.vClock_Symbol(2 + 2 * 22, 2);
sprintf(Clock_Arr, "%02d", Clock_Data.min);
Oled_Data.vOled_Asc_19x24(2 + 2 * 22 + 2 + 7, 2, Clock_Arr);
if(Admin_Data.Administrator_Flag)
{
Oled_Data.vOled_Display_Gb2312_String(54, 6, (uint8_t *)"★"); // 表示当前为管理员模式
}
else
{
Oled_Data.vOled_Display_Gb2312_String(54, 6, (uint8_t *)" ");
}
while(1)
{
MyUSART1_Data.vUsart1_Rx_Data_Analytic(); // 上位机调试数据解析
MyUSART2_Data.vUsart2_Rx_Data_Analytic(); // WiFi数据数据解析
if(MyUSART2_Data.APP_Rx_Flag)
{
MyUSART2_Data.APP_Rx_Flag = 0;
return;
}
if(INTERFACE_6 == Menu_Data.Menu_State)
{
return;
}
// 进入管理员解锁页面(k1)
if (Key_Data.Key_Down_Buff[0])
{
Key_Data.Key_Down_Buff[0] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
MyAll_Data.vTime_Out_Init(); // 超时等待清0
if (Admin_Data.Administrator_Flag) // 已解锁,回到主页面
{
Key_Data.vKey_Flag_Init();
Menu_Data.vMenu_Admin_Pass_Interface();
}
else // 需要解锁
{
Menu_Data.Menu_State = INTERFACE_2;
Menu_Data.Menu_Flag_Buff[1] = 1;
}
return;
}
// 进入功能页面(k3),有些功能需要有管理员身份 Admin_Data.Administrator_Flag
if (Key_Data.Key_Down_Buff[2])
{
Key_Data.Key_Down_Buff[2] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
MyAll_Data.vTime_Out_Init(); // 超时等待清0
Menu_Data.Menu_State = INTERFACE_3;
Menu_Data.Menu_Flag_Buff[2] = 1;
return;
}
if(AS608_Data.AS608_Wak_Flag)
{
AS608_Data.AS608_Wak_Flag = 0;
Menu_Data.Menu_Flag_Buff[6] = 1;
Menu_Data.Menu_State = INTERFACE_7;
return;
}
if (Save_Time != Clock_Data.sec)
{
Save_Time = Clock_Data.sec; // 更新保存的秒数
// printf("Old:%d New:%d\r\n",Save_Time,Clock_Data.sec);
y = 0; // 置 y 为 0,用于后面的位移操作
// 将当前秒和下一秒的秒数分别转换成两个字符,并将它们转换成对应的位图矩阵存储到 num_buf 数组中
sprintf(Clock_Arr, "%02d", Clock_Data.sec);
// printf("%d\r\n",Clock_Data.sec);
for (x = 0; x < 11; x++)
{
num_buf[0][x] = F11x16[Clock_Arr[0] - 0x30][x] | (uint16_t)F11x16[Clock_Arr[0] - 0x30][11 + x] << 8; // 将第一个数字的位图矩阵存储到 num_buf[0] 中
num_buf[1][x] = F11x16[Clock_Arr[1] - 0x30][x] | (uint16_t)F11x16[Clock_Arr[1] - 0x30][11 + x] << 8; // 将第二个数字的位图矩阵存储到 num_buf[1] 中
}
sprintf(Clock_Arr, "%02d", (Clock_Data.sec + 1) % 60);
for (x = 0; x < 11; x++)
{
num_buf[2][x] = F11x16[Clock_Arr[0] - 0x30][x] | (uint16_t)F11x16[Clock_Arr[0] - 0x30][11 + x] << 8; // 将下一个秒数的第一个数字的位图矩阵存储到 num_buf[2] 中
num_buf[3][x] = F11x16[Clock_Arr[1] - 0x30][x] | (uint16_t)F11x16[Clock_Arr[1] - 0x30][11 + x] << 8; // 将下一个秒数的第二个数字的位图矩阵存储到 num_buf[3] 中
}
// 将当前分钟数和下一分钟数分别转换成两个字符,并将它们转换成对应的位图矩阵存储到 num1_buf 数组中
sprintf(Clock_Arr, "%02d", Clock_Data.min);
for (x = 0; x < 19; x++)
{
num1_buf[0][x] = F19x24[Clock_Arr[0] - 0x30][x] | (uint32_t)F19x24[Clock_Arr[0] - 0x30][1 * 19 + x] << 8 * 1 | (uint32_t)F19x24[Clock_Arr[0] - 0x30][2 * 19 + x] << 8 * 2; // 将第一个数字的位图矩阵存储到 num1_buf[0] 中
num1_buf[1][x] = F19x24[Clock_Arr[1] - 0x30][x] | (uint32_t)F19x24[Clock_Arr[1] - 0x30][1 * 19 + x] << 8 * 1 | (uint32_t)F19x24[Clock_Arr[1] - 0x30][2 * 19 + x] << 8 * 2; // 将第二个数字的位图矩阵存储到 num1_buf[1] 中
}
sprintf(Clock_Arr, "%02d", (Clock_Data.min + 1) % 60);
for (x = 0; x < 19; x++)
{
num1_buf[2][x] = F19x24[Clock_Arr[0] - 0x30][x] | (uint32_t)F19x24[Clock_Arr[0] - 0x30][1 * 19 + x] << 8 * 1 | (uint32_t)F19x24[Clock_Arr[0] - 0x30][2 * 19 + x] << 8 * 2; // 将下一个分钟数的第一个数字的位图矩阵存储到 num1_buf[2] 中
num1_buf[3][x] = F19x24[Clock_Arr[1] - 0x30][x] | (uint32_t)F19x24[Clock_Arr[1] - 0x30][1 * 19 + x] << 8 * 1 | (uint32_t)F19x24[Clock_Arr[1] - 0x30][2 * 19 + x] << 8 * 2; // 将下一个分钟数的第二个数字的位图矩阵存储到 num1_buf[3] 中
}
// 将当前小时数和下一个小时数分别转换成两个字符,并将它们转换成对应的位图矩阵存储到 num2_buf 数组中
sprintf(Clock_Arr, "%02d", Clock_Data.hour);
for (x = 0; x < 19; x++)
{
num2_buf[0][x] = F19x24[Clock_Arr[0] - 0x30][x] | (uint32_t)F19x24[Clock_Arr[0] - 0x30][1 * 19 + x] << 8 * 1 | (uint32_t)F19x24[Clock_Arr[0] - 0x30][2 * 19 + x] << 8 * 2; // 将第一个数字的位图矩阵存储到 num2_buf[0] 中
num2_buf[1][x] = F19x24[Clock_Arr[1] - 0x30][x] | (uint32_t)F19x24[Clock_Arr[1] - 0x30][1 * 19 + x] << 8 * 1 | (uint32_t)F19x24[Clock_Arr[1] - 0x30][2 * 19 + x] << 8 * 2; // 将第二个数字的位图矩阵存储到 num2_buf[1] 中
}
sprintf(Clock_Arr, "%02d", (Clock_Data.hour + 1) % 24);
for (x = 0; x < 19; x++)
{
num2_buf[2][x] = F19x24[Clock_Arr[0] - 0x30][x] | (uint32_t)F19x24[Clock_Arr[0] - 0x30][1 * 19 + x] << 8 * 1 | (uint32_t)F19x24[Clock_Arr[0] - 0x30][2 * 19 + x] << 8 * 2; // 将下一个小时数的第一个数字的位图矩阵存储到 num2_buf[2] 中
num2_buf[3][x] = F19x24[Clock_Arr[1] - 0x30][x] | (uint32_t)F19x24[Clock_Arr[1] - 0x30][1 * 19 + x] << 8 * 1 | (uint32_t)F19x24[Clock_Arr[1] - 0x30][2 * 19 + x] << 8 * 2; // 将下一个小时数的第二个数字的位图矩阵存储到 num2_buf[3] 中
}
}
// 如果 y 小于 19,表示数字时钟的显示还没有到达顶部
if (y < 19)
{
for (x = 0; x < 11; x++)
{
if (y > 2) // 如果当前显示的行数大于 2,表示数字时钟的显示位于中心区域
{
// 分两种情况更新位图矩阵:
// 当前秒数的个位是 9 时,需要将个位的数字从上方更新到下方,将下一秒的个位数字从下方更新到上方
// 当前秒数的个位不是 9 时,只需要将个位的数字从上方更新到下方
if (9 == Clock_Data.sec % 10)
{
num_buf[0][x] = (num_buf[0][x] >> 1) | ((num_buf[2][x] & 0x01) << 15); // 将当前秒数的个位数字从上方更新到下方
num_buf[1][x] = (num_buf[1][x] >> 1) | ((num_buf[3][x] & 0x01) << 15); // 将当前秒数的十位数字从上方更新到下方
num_buf[2][x] = num_buf[2][x] >> 1; // 将下一秒的个位数字从下方更新到上方
num_buf[3][x] = num_buf[3][x] >> 1; // 将下一秒的十位数字从下方更新到上方
}
else
{
num_buf[1][x] = (num_buf[1][x] >> 1) | ((num_buf[3][x] & 0x01) << 15); // 将下一秒的十位数字从下方更新到上方
num_buf[3][x] = num_buf[3][x] >> 1; // 将下一秒的十位数字从下方更新到上方
}
}
else // 将下一秒的十位数字从下方更新到上方
{
if (9 == Clock_Data.sec % 10) // 如果当前秒数的个位是 9,需要将个位的数字从上方更新到下方
{
num_buf[0][x] = (num_buf[0][x] >> 1); // 将当前秒数的个位数字从上方更新到下方
}
num_buf[1][x] = (num_buf[1][x] >> 1); // 将当前秒数的个位数字从上方更新到下方
}
}
// 更新数字时钟的显示
// 将数字时钟矩阵的数据写入 OLED 显示屏的相应位置,以更新数字时钟的显示
Oled_Data.vOled_Set_Address(2 + 4 * 22 + 2 + 7, 3); //设置坐标
for (x = 0; x < 11; x++)
{
Oled_Data.vOled_Write_Byte(num_buf[0][x], 1);
}
Oled_Data.vOled_Set_Address(2 + 4 * 22 + 2 + 7, 4);
for (x = 0; x < 11; x++)
{
Oled_Data.vOled_Write_Byte(num_buf[0][x] >> 8, 1);
}
Oled_Data.vOled_Set_Address(2 + 4 * 22 + 2 + 7 + 12, 3); //设置坐标
for (x = 0; x < 11; x++)
{
Oled_Data.vOled_Write_Byte(num_buf[1][x], 1);
}
Oled_Data.vOled_Set_Address(2 + 4 * 22 + 2 + 7 + 12, 4);
for (x = 0; x < 11; x++)
{
Oled_Data.vOled_Write_Byte(num_buf[1][x] >> 8, 1);
}
}
// 如果当前显示未到达底部并且秒数为 59,则进入更新数字分钟的代码块
if ((y < 27) && (59 == Clock_Data.sec))
{
for (x = 0; x < 19; x++)
{
if (y > 2)// 如果当前显示的行数大于 2,表示数字分钟的显示位于中心区域
{
// 分两种情况更新位图矩阵:
// 当前分钟数的个位是 9 时,需要将个位的数字从上方更新到下方,将下一分钟的个位数字从下方更新到上方
// 当前分钟数的个位不是 9 时,只需要将个位的数字从上方更新到下方
if (9 == (Clock_Data.min % 10))
{
num1_buf[0][x] = (num1_buf[0][x] >> 1) | ((num1_buf[2][x] & 0x01) << 23);// 将当前分钟数的个位数字从上方更新到下方
num1_buf[1][x] = (num1_buf[1][x] >> 1) | ((num1_buf[3][x] & 0x01) << 23);// 将当前分钟数的十位数字从上方更新到下方
num1_buf[2][x] = num1_buf[2][x] >> 1;// 将下一分钟的个位数字从下方更新到上方
num1_buf[3][x] = num1_buf[3][x] >> 1;// 将下一分钟的十位数字从下方更新到上方
}
else
{
num1_buf[1][x] = (num1_buf[1][x] >> 1) | ((num1_buf[3][x] & 0x01) << 23);// 将当前分钟数的十位数字从上方更新到下方
num1_buf[3][x] = num1_buf[3][x] >> 1;// 将下一分钟的十位数字从下方更新到上方
}
}
else// 否则,表示数字分钟的显示位于顶部或底部
{
if (9 == (Clock_Data.min % 10)) // 如果当前分钟数的个位是 9,需要将个位的数字从上方更新到下方
{
num1_buf[0][x] = (num1_buf[0][x] >> 1);// 将当前分钟数的个位数字从上方更新到下方
}
num1_buf[1][x] = (num1_buf[1][x] >> 1);// 将当前分钟数的十位数字从上方更新到下方
}
}
// Asc19_24(2+2*22+2+7,2,s);
// 更新数字分钟的显示
// 将数字分钟矩阵的数据写入 OLED 显示屏的相应位置,以更新数字分钟的显示
Oled_Data.vOled_Set_Address(2 + 2 * 22 + 2 + 7, 2); //设置坐标
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num1_buf[0][x], 1);
}
Oled_Data.vOled_Set_Address(2 + 2 * 22 + 2 + 7, 3);
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num1_buf[0][x] >> 8, 1);
}
Oled_Data.vOled_Set_Address(2 + 2 * 22 + 2 + 7, 4);
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num1_buf[0][x] >> 16, 1);
}
Oled_Data.vOled_Set_Address(2 + 3 * 22 + 2 + 7, 2); // 设置坐标
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num1_buf[1][x], 1);
}
Oled_Data.vOled_Set_Address(2 + 3 * 22 + 2 + 7, 3); // 设置坐标
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num1_buf[1][x] >> 8, 1);
}
Oled_Data.vOled_Set_Address(2 + 3 * 22 + 2 + 7, 4); // 设置坐标
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num1_buf[1][x] >> 16, 1);
}
}
// 如果当前显示未到达底部,并且分钟数和秒数都为 59,则进行数字小时的更新
if ((y < 27) && (Clock_Data.min == 59) && (Clock_Data.sec == 59))
{
for (x = 0; x < 19; x++)
{
if (y > 2)// 如果当前显示的行数大于 2,表示数字小时的显示位于中心区域
{
// 根据数字小时的个位数是否为 9 或当前时间是否为 23 时判断具体更新方式:
// 若数字小时的个位数为 9 或当前时间为 23,则需将个位数字从上方更新到下方,将下一小时的个位数字从下方更新到上方;
// 否则,只需将个位数字从上方更新到下方。
if ((Clock_Data.hour % 10 == 9) || (Clock_Data.hour == 23))
{
num2_buf[0][x] = (num2_buf[0][x] >> 1) | ((num2_buf[2][x] & 0x01) << 23);// 将当前小时数的个位数字从上方更新到下方
num2_buf[1][x] = (num2_buf[1][x] >> 1) | ((num2_buf[3][x] & 0x01) << 23);// 将当前小时数的十位数字从上方更新到下方
num2_buf[2][x] = num2_buf[2][x] >> 1;// 将下一小时的个位数字从下方更新到上方
num2_buf[3][x] = num2_buf[3][x] >> 1;// 将下一小时的十位数字从下方更新到上方
}
else
{
num2_buf[1][x] = (num2_buf[1][x] >> 1) | ((num2_buf[3][x] & 0x01) << 23);// 将当前小时数的十位数字从上方更新到下方
num2_buf[3][x] = num2_buf[3][x] >> 1;// 将下一小时的十位数字从下方更新到上方
}
}
else// 否则,表示数字小时的显示位于顶部或底部
{
if (9 == Clock_Data.hour % 10)
{
num2_buf[0][x] = (num2_buf[0][x] >> 1); // 将当前小时数的个位数字从上方更新到下方
}
num2_buf[1][x] = (num2_buf[1][x] >> 1);// 将当前小时数的十位数字从上方更新到下方
}
}
// Asc19_24(2+2*22+2+7,2,s);
Oled_Data.vOled_Set_Address(2, 2); // 设置坐标
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num2_buf[0][x], 1);
}
Oled_Data.vOled_Set_Address(2, 3);
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num2_buf[0][x] >> 8, 1);
}
Oled_Data.vOled_Set_Address(2, 4);
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num2_buf[0][x] >> 16, 1);
}
Oled_Data.vOled_Set_Address(2 + 1 * 22, 2); // 设置坐标
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num2_buf[1][x], 1);
}
Oled_Data.vOled_Set_Address(2 + 1 * 22, 3);
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num2_buf[1][x] >> 8, 1);
}
Oled_Data.vOled_Set_Address(2 + 1 * 22, 4);
for (x = 0; x < 19; x++)
{
Oled_Data.vOled_Write_Byte(num2_buf[1][x] >> 16, 1);
}
}
y++; // 行数加 1
HAL_Delay(17); // 延时,使 OLED 显示屏能够刷新完整屏幕
}
}
/*
* @description: 获取当前时间日期
* @param {RTC_TimeTypeDef} *sTime 要存到哪
* @return {*}
* @Date: 2023-04-25 16:42:55
*/
// 获取当前时间日期
void vClock_Get_Time_Date(RTC_TimeTypeDef *sTime)
{
HAL_RTC_GetTime(&hrtc, &rtc_Time, RTC_FORMAT_BIN); // 获取时间
HAL_RTC_GetDate(&hrtc, &rtc_Date, RTC_FORMAT_BIN); // 获取日期
Clock_Data.sec = sTime->Seconds;
Clock_Data.min = sTime->Minutes;
Clock_Data.hour = sTime->Hours;
}
/*
* @description: RTC设置时间
* @return {*}
* @Date: 2023-04-25 17:55:53
*/
// RTC设置时间
void vClock_Set_Time(uint8_t h, uint8_t m, uint8_t s)
{
rtc_Time.Hours = h;
rtc_Time.Minutes = m;
rtc_Time.Seconds = s;
rtc_Date.Year = 23;
rtc_Date.Month = 4;
rtc_Date.Date = 25;
HAL_RTC_SetDate(&hrtc, &rtc_Date, RTC_FORMAT_BIN);
HAL_RTC_SetTime(&hrtc, &rtc_Time, RTC_FORMAT_BIN);
}
Oled.h
/*
*@Description: OLED屏幕
*@Author: Yang
*@Date: 2023-04-21 15:17:10
*/
#ifndef __OLED_H
#define __OLED_H
#include "MyAll.h"
/******引脚定义*******/
// OLED SCL引脚PA0
#define OLED_SCL GPIO_PIN_0
// OLED SDA引脚PA1
#define OLED_SDA GPIO_PIN_1
// OLED DC引脚PA2
#define OLED_DC GPIO_PIN_2
// OLED CS1引脚PA3
#define OLED_CS1 GPIO_PIN_3
// OLED FS0引脚PA4
#define OLED_FS0 GPIO_PIN_4
// OLED CS2引脚PA5
#define OLED_CS2 GPIO_PIN_5
/******引脚置位/读取*******/
// SCL 置0/1
#define SCL_OUT_0 HAL_GPIO_WritePin(GPIOA,OLED_SCL,GPIO_PIN_RESET)
#define SCL_OUT_1 HAL_GPIO_WritePin(GPIOA,OLED_SCL,GPIO_PIN_SET)
// SDA 置0/1
#define SDA_OUT_0 HAL_GPIO_WritePin(GPIOA,OLED_SDA,GPIO_PIN_RESET)
#define SDA_OUT_1 HAL_GPIO_WritePin(GPIOA,OLED_SDA,GPIO_PIN_SET)
// DC 置0/1
#define DC_OUT_0 HAL_GPIO_WritePin(GPIOA,OLED_DC,GPIO_PIN_RESET)
#define DC_OUT_1 HAL_GPIO_WritePin(GPIOA,OLED_DC,GPIO_PIN_SET)
// CS1 置0/1
#define CS1_OUT_0 HAL_GPIO_WritePin(GPIOA,OLED_CS1,GPIO_PIN_RESET)
#define CS1_OUT_1 HAL_GPIO_WritePin(GPIOA,OLED_CS1,GPIO_PIN_SET)
// FS0 读取电平状态
#define FS0_READ HAL_GPIO_ReadPin(GPIOA,OLED_FS0)
// CS2 置0/1
#define CS2_OUT_0 HAL_GPIO_WritePin(GPIOA,OLED_CS2,GPIO_PIN_RESET)
#define CS2_OUT_1 HAL_GPIO_WritePin(GPIOA,OLED_CS2,GPIO_PIN_SET)
/******其他*******/
// 写命令
#define OLED_CMD 0
// 写数据
#define OLED_DATA 1
// 正常
#define OLED_SET 1
// 反常
#define OLED_RESET 0
/******命令大全*******/
// 正常显示
#define CMD_NORMAL 0xA6
// 反转显示
#define CMD_INVERSE 0xA7
// 设置对比度控制(选择1~256,复位值0x7F)
#define CMD_SET_CONTRACT 0x7F
// 电压等级设置(0x00-0.65xVCC 0x20-0.77xVCC(复位值) 0x30-0.83xVCC)
#define CMD_SET_V_LEVEL 0x20
// 显示开启
#define CMD_DISPLAY_ON 0xAF
// 显示关闭(睡眠模式)
#define CMD_DISPLAY_OFF 0xAE
// 控制列地址0被映射到SEG0
#define CMD_RE_MAP_0 0xA0
// 控制列地址127被映射到SEG0
#define CMD_RE_MAP_127 0xA1
// 控制扫描方向COM0-->COM[N-1]
#define CMD_DIRECTION_L_TO_R 0xC0
// 控制扫描方向COM[N-1]-->COM0
#define CMD_DIRECTION_R_TO_L 0xC8
typedef struct
{
uint32_t Font_Addr;
void (*vOled_Write_Byte)(uint8_t,uint8_t);
void (*vOled_Toggle_Display)(uint8_t);
void (*vOled_Rotate_Display)(uint8_t);
void (*vOled_Clear)(void);
void (*vOled_Set_Address)(uint8_t,uint8_t);
void (*vOled_Display_128x64_Bmp)(uint8_t*);
void (*vOled_Display_Gb2312_String)(uint8_t,uint8_t,uint8_t*);
void (*vOled_Display_String_5x7)(uint8_t,uint8_t,uint8_t*);
void (*vOled_Init)(void);
void (*vOled_Draw_Bmp)(unsigned char,unsigned char,unsigned char,unsigned char,unsigned char []);
void (*vOled_Light_Row)(uint8_t,uint8_t);
void (*vOled_Asc_19x24)(uint8_t,uint8_t,uint8_t []);
void (*vOled_Painting_Pixel)(uint8_t,uint8_t,uint8_t);
}Oled_TypeDef;
extern Oled_TypeDef Oled_Data;
void vOled_Write_Byte(uint8_t data, uint8_t format);
void vOled_Toggle_Display(uint8_t swch);
void vOled_Rotate_Display(uint8_t swch);
void vOled_Set_Address(uint8_t x,uint8_t y);
void vOled_Display_128x64_Bmp(uint8_t *dp);
void vOled_Clear(void);
void vOled_Display_Gb2312_String(uint8_t x, uint8_t y, uint8_t *text);
void vOled_Display_5x7(uint8_t x, uint8_t y, uint8_t *dp);
void vOled_Init(void);
void vOled_Asc_19x24(uint8_t x, uint8_t y, uint8_t ch[]);
void vOled_Painting_Pixel(uint8_t x,uint8_t y,uint8_t num);
void vOled_Light_Row(uint8_t row,uint8_t swch);
void vOled_Draw_Bmp(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[]);
#endif
Oled.c
/*
*@Description: OLED屏幕 接线:CLK--PA0 MOS--PA1 DC--PA2 CS1--PA3 FS0--PA4 CS2--PA5
*@Author: Yang
*@Date: 2023-04-21 15:17:04
*/
#include "Oled.h"
/*====================================静态内部函数声明区 BEGIN====================================*/
static void svOled_DisPlay_Control(uint8_t swch);
static void svOled_Display_16x16(uint8_t x, uint8_t y, uint8_t *dp);
static void svOled_Display_8x16(uint8_t x, uint8_t y, uint8_t *dp);
static void svOled_Send_Command_To_Rom(uint8_t dat);
static uint8_t sucOled_Get_Data_From_Rom(void);
static void svOled_Get_Data(uint8_t addrHigh, uint8_t addrMid, uint8_t addrLow, uint8_t *pbuff, uint8_t DataLen);
static void vOled_Display_String_5x7(uint8_t x, uint8_t y, uint8_t *text);
static void svOled_Show_Decimal(uint8_t x, uint8_t y, float num1, uint8_t len);
/*====================================静态内部函数声明区 END====================================*/
/*====================================变量区 BEGIN====================================*/
extern const uint8_t F11x16[][22];
extern const uint8_t F19x24[][57];
Oled_TypeDef Oled_Data =
{
.Font_Addr = 0,
.vOled_Write_Byte = &vOled_Write_Byte,
.vOled_Toggle_Display = &vOled_Toggle_Display,
.vOled_Rotate_Display = &vOled_Rotate_Display,
.vOled_Clear = &vOled_Clear,
.vOled_Set_Address = &vOled_Set_Address,
.vOled_Display_128x64_Bmp = &vOled_Display_128x64_Bmp,
.vOled_Display_Gb2312_String = &vOled_Display_Gb2312_String,
.vOled_Display_String_5x7 = &vOled_Display_String_5x7,
.vOled_Init = &vOled_Init,
.vOled_Draw_Bmp = &vOled_Draw_Bmp,
.vOled_Light_Row = &vOled_Light_Row,
.vOled_Asc_19x24 = &vOled_Asc_19x24,
.vOled_Painting_Pixel = &vOled_Painting_Pixel
};
/*====================================变量区 END====================================*/
/*
* @description: 向SSD1306写入一个字节
* @param: 待写入数据
* @param: 格式-- OLED_CMD(命令) OLED_DATA(数据)
* @return: 无
* @Date: 2023-04-22 17:46:53
*/
// 向SSD1306写入一个字节
void vOled_Write_Byte(uint8_t data, uint8_t format)
{
if(OLED_DATA == format)
{
DC_OUT_1;
}
else
{
DC_OUT_0;
}
CS1_OUT_0;
for (uint8_t i = 0; i < 8; i++)
{
SCL_OUT_0;
if(data & 0x80)
{
SDA_OUT_1;
}
else
{
SDA_OUT_0;
}
SCL_OUT_1;
data <<= 1;
}
CS1_OUT_1;
DC_OUT_1;
}
/*
* @description: 反显函数
* @param : OLED_SET--正常显示 OLED_RESET--反色显示
* @return: 无
* @Date: 2023-04-22 17:40:54
*/
// 反显函数
void vOled_Toggle_Display(uint8_t swch)
{
if(OLED_SET == swch)
{
Oled_Data.vOled_Write_Byte(CMD_NORMAL, OLED_CMD); //正常显示
}
else if(OLED_RESET == swch)
{
Oled_Data.vOled_Write_Byte(CMD_INVERSE, OLED_CMD); //反转显示
}
}
/*
* @description: 屏幕旋转180度
* @param: OLED_SET--正常显示 OLED_RESET--旋转显示
* @return: 无
* @Date: 2023-04-22 18:32:16
*/
// 屏幕旋转180度
void vOled_Rotate_Display(uint8_t swch)
{
if(OLED_SET == swch)
{
Oled_Data.vOled_Write_Byte(CMD_DIRECTION_R_TO_L, OLED_CMD); //正常显示
Oled_Data.vOled_Write_Byte(CMD_RE_MAP_127, OLED_CMD);
}
else if(OLED_RESET == swch)
{
Oled_Data.vOled_Write_Byte(CMD_DIRECTION_L_TO_R, OLED_CMD); //旋转显示
Oled_Data.vOled_Write_Byte(CMD_RE_MAP_0, OLED_CMD);
}
}
/*
* @description: OLED显示控制
* @param: OLED_SET--显示 OLED_RESET--不显示
* @return: 无
* @Date: 2023-04-22 18:36:36
*/
// OLED显示控制
static void svOled_DisPlay_Control(uint8_t swch)
{
if (OLED_SET == swch)
{
Oled_Data.vOled_Write_Byte(0x8D, OLED_CMD); // 电荷泵使能
Oled_Data.vOled_Write_Byte(0x14, OLED_CMD); // 开启电荷泵
Oled_Data.vOled_Write_Byte(CMD_DISPLAY_ON, OLED_CMD); // 点亮屏幕
}
else if (OLED_RESET == swch)
{
Oled_Data.vOled_Write_Byte(0x8D, OLED_CMD); // 电荷泵使能
Oled_Data.vOled_Write_Byte(0x10, OLED_CMD); // 关闭电荷泵
Oled_Data.vOled_Write_Byte(CMD_DISPLAY_OFF, OLED_CMD); // 关闭屏幕
}
}
/*
* @description: 清屏函数
* @return: 无
* @Date: 2023-04-22 18:41:02
*/
// 清屏函数
void vOled_Clear(void)
{
uint8_t i, n;
for (i = 0; i < 8; i++)
{
Oled_Data.vOled_Write_Byte(0xb0 + i, OLED_CMD); // 设置页地址
Oled_Data.vOled_Write_Byte(0x10, OLED_CMD); // 设置列地址的高4位
Oled_Data.vOled_Write_Byte(0x02, OLED_CMD); // 设置列地址的低4位
for (n = 0; n < 128; n++)
{
Oled_Data.vOled_Write_Byte(0x00, OLED_DATA); // 清除所有数据
}
}
}
/*
* @description: 设置光标位置
* @param {uint8_t} x
* @param {uint8_t} y
* @return: 无
* @Date: 2023-04-22 18:45:13
*/
// 设置光标位置
void vOled_Set_Address(uint8_t x, uint8_t y)
{
x += 2;
Oled_Data.vOled_Write_Byte(0xb0 + y, OLED_CMD); //设置页地址
Oled_Data.vOled_Write_Byte(((x & 0xf0) >> 4) | 0x10, OLED_CMD); //设置列地址的高4位
Oled_Data.vOled_Write_Byte((x & 0x0f), OLED_CMD); //设置列地址的低4位
}
/*
* @description: 显示128x64点阵图像
* @param {uint8_t} *dp
* @return: 无
* @Date: 2023-04-22 18:52:20
*/
// 显示128x64点阵图像
void vOled_Display_128x64_Bmp(uint8_t *dp)
{
uint8_t i, j;
for(i = 0; i < 8; i++)
{
Oled_Data.vOled_Set_Address(0, i);
for(j = 0; j < 128; j++)
{
Oled_Data.vOled_Write_Byte(*dp, OLED_DATA); //写数据到OLED,每写完一个8位的数据后列地址自动加1
dp++;
}
}
}
/*
* @description: 显示16x16点阵图像、汉字、生僻字或16x16点阵的其他图标
* @param {uint8_t} x
* @param {uint8_t} y
* @param {uint8_t} *dp
* @return: 无
* @Date: 2023-04-22 18:54:23
*/
// 显示16x16点阵图像、汉字、生僻字或16x16点阵的其他图标
static void svOled_Display_16x16(uint8_t x, uint8_t y, uint8_t *dp)
{
uint8_t i, j;
for (j = 0; j < 2; j++)
{
Oled_Data.vOled_Set_Address(x, y);
for (i = 0; i < 16; i++)
{
Oled_Data.vOled_Write_Byte(*dp, OLED_DATA); // 写数据到OLED,每写完一个8位的数据后列地址自动加1
dp++;
}
y++;
}
}
/*
* @description: 显示8x16点阵图像、ASCII, 或8x16点阵的自造字符、其他图标
* @param {uint8_t} x
* @param {uint8_t} y
* @param {uint8_t} *dp
* @return: 无
* @Date: 2023-04-22 18:55:58
*/
// 显示8x16点阵图像、ASCII, 或8x16点阵的自造字符、其他图标
static void svOled_Display_8x16(uint8_t x, uint8_t y, uint8_t *dp)
{
uint8_t i, j;
for (j = 0; j < 2; j++)
{
Oled_Data.vOled_Set_Address(x, y);
for (i = 0; i < 8; i++)
{
Oled_Data.vOled_Write_Byte(*dp, OLED_DATA); // 写数据到LCD,每写完一个8位的数据后列地址自动加1
dp++;
}
y++;
}
}
/*
* @description: 显示5*7点阵图像、ASCII, 或5x7点阵的自造字符、其他图标
* @param {uint8_t} x
* @param {uint8_t} y
* @param {uint8_t} *dp
* @return: 无
* @Date: 2023-04-22 18:57:55
*/
// 显示5*7点阵图像、ASCII, 或5x7点阵的自造字符、其他图标
void vOled_Display_5x7(uint8_t x, uint8_t y, uint8_t *dp)
{
uint8_t i;
Oled_Data.vOled_Set_Address(x, y);
for (i = 0; i < 6; i++)
{
Oled_Data.vOled_Write_Byte(*dp, OLED_DATA);
dp++;
}
}
/*
* @description: 送指令到晶联讯字库IC
* @param {uint8_t} dat
* @return: 无
* @Date: 2023-04-22 19:05:28
*/
// 送指令到晶联讯字库IC
static void svOled_Send_Command_To_Rom(uint8_t dat)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
SCL_OUT_0;
if (dat & 0x80)
{
SDA_OUT_1;
}
else
{
SDA_OUT_0;
}
dat <<= 1;
SCL_OUT_1;
}
}
/*
* @description: 从晶联讯字库IC中取汉字或字符数据(1个字节)
* @return {*}
* @Date: 2023-04-22 19:13:07
*/
// 从晶联讯字库IC中取汉字或字符数据(1个字节)
static uint8_t sucOled_Get_Data_From_Rom(void)
{
uint8_t i, read = 0;
for (i = 0; i < 8; i++)
{
SCL_OUT_0;
read <<= 1;
if (FS0_READ)
{
read++;
}
SCL_OUT_1;
}
return read;
}
/*
* @description: 连续读取
* @param {uint8_t} addrHigh
* @param {uint8_t} addrMid
* @param {uint8_t} addrLow
* @param {uint8_t} *pbuff
* @param {uint8_t} DataLen
* @return: 无
* @Date: 2023-04-22 19:20:35
*/
// 连续读取(从相关地址(addrHigh:地址高字节,addrMid:地址中字节,addrLow:地址低字节)中连续读出DataLen个字节的数据到 pbuff的地址)
static void svOled_Get_Data(uint8_t addrHigh, uint8_t addrMid, uint8_t addrLow, uint8_t *pbuff, uint8_t DataLen)
{
uint8_t i;
CS2_OUT_0;
svOled_Send_Command_To_Rom(0x03);
svOled_Send_Command_To_Rom(addrHigh);
svOled_Send_Command_To_Rom(addrMid);
svOled_Send_Command_To_Rom(addrLow);
for (i = 0; i < DataLen; i++)
{
*(pbuff + i) = sucOled_Get_Data_From_Rom();
}
CS2_OUT_1;
}
/*
* @description: 指定坐标显示GB2312字符串(16*16)
* @param {uint8_t} x: 范围0~128
* @param {uint8_t} y: 范围0~6
* @param {uint8_t} *text : 要显示的字符串
* @return: 无
* @Date: 2023-04-22 19:36:42
*/
// 指定坐标显示GB2312字符串(16*16)
void vOled_Display_Gb2312_String(uint8_t x, uint8_t y, uint8_t *text)
{
uint8_t i = 0;
uint8_t addrHigh, addrMid, addrLow;
uint8_t fontbuf[32];
while (text[i] > 0x00)
{
if ((text[i] >= 0xb0) && (text[i] <= 0xf7) && (text[i + 1] >= 0xa1))
{
// 国标简体(GB2312)汉字在晶联讯字库IC中的地址由以下公式来计算:
// Address = ((MSB - 0xB0) * 94 + (LSB - 0xA1)+ 846)*32+ BaseAdd;BaseAdd=0
// 由于担心8位单片机有乘法溢出问题,所以分三部取地址
Oled_Data.Font_Addr = (text[i] - 0xb0) * 94;
Oled_Data.Font_Addr += (text[i + 1] - 0xa1) + 846;
Oled_Data.Font_Addr = (Oled_Data.Font_Addr) * 32;
addrHigh = (Oled_Data.Font_Addr & 0xff0000) >> 16; // 地址的高8位,共24位
addrMid = (Oled_Data.Font_Addr & 0xff00) >> 8; // 地址的中8位,共24位
addrLow = (Oled_Data.Font_Addr & 0xff); // 地址的低8位,共24位
svOled_Get_Data(addrHigh, addrMid, addrLow, fontbuf, 32);
// 取32个字节的数据,存到"fontbuf[32]"
svOled_Display_16x16(x, y, fontbuf);
// 显示汉字到LCD上,y为页地址,x为列地址,fontbuf[]为数据
x += 16;
i += 2;
}
else if ((text[i] >= 0xa1) && (text[i] <= 0xa3) && (text[i + 1] >= 0xa1))
{
Oled_Data.Font_Addr = (text[i] - 0xa1) * 94;
Oled_Data.Font_Addr += (text[i + 1] - 0xa1);
Oled_Data.Font_Addr = (Oled_Data.Font_Addr) * 32;
addrHigh = (Oled_Data.Font_Addr & 0xff0000) >> 16;
addrMid = (Oled_Data.Font_Addr & 0xff00) >> 8;
addrLow = (Oled_Data.Font_Addr & 0xff);
svOled_Get_Data(addrHigh, addrMid, addrLow, fontbuf, 32);
svOled_Display_16x16(x, y, fontbuf);
x += 16;
i += 2;
}
else if ((text[i] >= 0x20) && (text[i] <= 0x7e))
{
unsigned char fontbuf[16];
Oled_Data.Font_Addr = (text[i] - 0x20);
Oled_Data.Font_Addr = (unsigned long)(Oled_Data.Font_Addr * 16);
Oled_Data.Font_Addr = (unsigned long)(Oled_Data.Font_Addr + 0x3cf80);
addrHigh = (Oled_Data.Font_Addr & 0xff0000) >> 16;
addrMid = (Oled_Data.Font_Addr & 0xff00) >> 8;
addrLow = Oled_Data.Font_Addr & 0xff;
svOled_Get_Data(addrHigh, addrMid, addrLow, fontbuf, 16);
svOled_Display_8x16(x, y, fontbuf);
x += 8;
i += 1;
}
else
i++;
}
}
/*
* @description: 指定坐标显示字符串(5*7)
* @param {uint8_t} x
* @param {uint8_t} y
* @param {uint8_t} *text
* @return: 无
* @Date: 2023-04-22 19:41:51
*/
// 指定坐标显示字符串(5*7)
static void vOled_Display_String_5x7(uint8_t x, uint8_t y, uint8_t *text)
{
uint8_t i = 0;
uint8_t addrHigh, addrMid, addrLow;
while (text[i] > 0x00)
{
if ((text[i] >= 0x20) && (text[i] <= 0x7e))
{
uint8_t fontbuf[8];
Oled_Data.Font_Addr = (text[i] - 0x20);
Oled_Data.Font_Addr = (unsigned long)(Oled_Data.Font_Addr * 8);
Oled_Data.Font_Addr = (unsigned long)(Oled_Data.Font_Addr + 0x3bfc0);
addrHigh = (Oled_Data.Font_Addr & 0xff0000) >> 16;
addrMid = (Oled_Data.Font_Addr & 0xff00) >> 8;
addrLow = Oled_Data.Font_Addr & 0xff;
svOled_Get_Data(addrHigh, addrMid, addrLow, fontbuf, 8);
vOled_Display_5x7(x, y, fontbuf);
x += 6;
i += 1;
}
else
i++;
}
}
/*
* @description: 显示2个数字(小数)
* @param: 起点坐标x
* @param: 起点坐标y
* @param: 要显示的小数
* @param: 数字的位数
* @return: 无
* @Date: 2023-04-22 19:49:24
*/
// 显示2个数字(小数)
static void svOled_Show_Decimal(uint8_t x, uint8_t y, float num1, uint8_t len)
{
uint8_t i;
uint32_t t, num;
x = x + len * 8 + 8; // 要显示的小数最低位的横坐标
num = num1 * 100; // 将小数左移两位并转化为整数
Oled_Data.vOled_Display_Gb2312_String(x - 24, y, (uint8_t *)"."); // 显示小数点
for (i = 0; i < len; i++)
{
t = num % 10; // 取个位数的数值
num = num / 10; // 将整数右移一位
x -= 8;
if (2 == i)
{
x -= 8;
}
// 当显示出来两个小数之后,空出小数点的位置
switch (t)
{
case 0:
Oled_Data.vOled_Display_Gb2312_String(x, y, (uint8_t *)"0");
break;
case 1:
Oled_Data.vOled_Display_Gb2312_String(x, y, (uint8_t *)"1");
break;
case 2:
Oled_Data.vOled_Display_Gb2312_String(x, y, (uint8_t *)"2");
break;
case 3:
Oled_Data.vOled_Display_Gb2312_String(x, y, (uint8_t *)"3");
break;
case 4:
Oled_Data.vOled_Display_Gb2312_String(x, y, (uint8_t *)"4");
break;
case 5:
Oled_Data.vOled_Display_Gb2312_String(x, y, (uint8_t *)"5");
break;
case 6:
Oled_Data.vOled_Display_Gb2312_String(x, y, (uint8_t *)"6");
break;
case 7:
Oled_Data.vOled_Display_Gb2312_String(x, y, (uint8_t *)"7");
break;
case 8:
Oled_Data.vOled_Display_Gb2312_String(x, y, (uint8_t *)"8");
break;
case 9:
Oled_Data.vOled_Display_Gb2312_String(x, y, (uint8_t *)"9");
break;
}
}
}
/*
* @description: OLED的初始化
* @return: 无
* @Date: 2023-04-22 19:50:58
*/
// OLED的初始化
void vOled_Init(void)
{
HAL_Delay(200);
Oled_Data.vOled_Write_Byte(CMD_DISPLAY_OFF, OLED_CMD); /*display off*/
Oled_Data.vOled_Write_Byte(0x02, OLED_CMD); /*set lower column address*/
Oled_Data.vOled_Write_Byte(0x10, OLED_CMD); /*set higher column address*/
Oled_Data.vOled_Write_Byte(0x40, OLED_CMD); /*set display start line*/
Oled_Data.vOled_Write_Byte(0xB0, OLED_CMD); /*set page address*/
Oled_Data.vOled_Write_Byte(0x81, OLED_CMD); /*contract control*/
Oled_Data.vOled_Write_Byte(0xcf, OLED_CMD); /*128*/
Oled_Data.vOled_Write_Byte(CMD_RE_MAP_127, OLED_CMD); /*set segment remap*/
Oled_Data.vOled_Write_Byte(CMD_NORMAL, OLED_CMD); /*normal / reverse*/
Oled_Data.vOled_Write_Byte(0xA8, OLED_CMD); /*multiplex ratio*/
Oled_Data.vOled_Write_Byte(0x3F, OLED_CMD); /*duty = 1/64*/
Oled_Data.vOled_Write_Byte(0xad, OLED_CMD); /*set charge pump enable*/
Oled_Data.vOled_Write_Byte(0x8b, OLED_CMD); /* 0x8B 内供 VCC */
Oled_Data.vOled_Write_Byte(0x33, OLED_CMD); /*0X30---0X33 set VPP 9V */
Oled_Data.vOled_Write_Byte(CMD_DIRECTION_R_TO_L, OLED_CMD); /*Com scan direction*/
Oled_Data.vOled_Write_Byte(0xD3, OLED_CMD); /*set display offset*/
Oled_Data.vOled_Write_Byte(0x00, OLED_CMD); /* 0x20 */
Oled_Data.vOled_Write_Byte(0xD5, OLED_CMD); /*set osc division*/
Oled_Data.vOled_Write_Byte(0x80, OLED_CMD);
Oled_Data.vOled_Write_Byte(0xD9, OLED_CMD); /*set pre-charge period*/
Oled_Data.vOled_Write_Byte(0x1f, OLED_CMD); /*0x22*/
Oled_Data.vOled_Write_Byte(0xDA, OLED_CMD); /*set COM pins*/
Oled_Data.vOled_Write_Byte(0x12, OLED_CMD);
Oled_Data.vOled_Write_Byte(0xdb, OLED_CMD); /*set vcomh*/
Oled_Data.vOled_Write_Byte(0x40, OLED_CMD);
Oled_Data.vOled_Clear();
Oled_Data.vOled_Write_Byte(CMD_DISPLAY_ON, OLED_CMD); /*display ON*/
}
/*
* @description: 指定大小显示BMP
* @param {unsigned char} x0
* @param {unsigned char} y0
* @param {unsigned char} x1
* @param {unsigned char} y1
* @param {unsigned char} BMP
* @return: 无
* @Date: 2023-04-23 11:02:48
*/
// 指定大小显示BMP
void vOled_Draw_Bmp(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[])
{
unsigned int j = 0;
unsigned char x, y;
if (y1 % 8 == 0)
{
y = y1 / 8;
}
else
{
y = y1 / 8 + 1;
}
for (y = y0; y < y1; y++)
{
Oled_Data.vOled_Set_Address(x0, y);
for (x = x0; x < x1; x++)
{
Oled_Data.vOled_Write_Byte(BMP[j++], OLED_DATA);
}
}
}
/*
* @description: 点亮/熄灭OLED屏幕某一行像素
* @param {uint8_t} row
* @return {*}
* @Date: 2023-04-23 18:48:18
*/
// 点亮/熄灭OLED屏幕某一行像素
void vOled_Light_Row(uint8_t row, uint8_t swch)
{
// 设置OLED显示范围为第row行(row从0开始计算)
Oled_Data.vOled_Set_Address(0, row);
// 向OLED写入每个像素点的数据,可以根据需要修改该部分代码
for (uint8_t col = 0; col < 128; col++)
{
if(SET == swch)
{
Oled_Data.vOled_Write_Byte(0x01, OLED_DATA); // 写入0x01表示该像素点为白色(0xFF则表示8行像素即1列大小)
}
else
{
Oled_Data.vOled_Write_Byte(0x00, OLED_DATA); // 灭
}
}
}
/*
* @description: 写入一组标准ASCII字符串 19x24 数字字符
* @param {uint8_t} x 范围0~128
* @param {uint8_t} y 范围0~7
* @param {uint8_t} ch 要显示的字符串
* @return {*} 无
* @Date: 2023-04-25 14:53:14
*/
// 写入一组标准ASCII字符串 19x24 数字字符
void vOled_Asc_19x24(uint8_t x, uint8_t y, uint8_t ch[])
{
uint8_t c = 0, i = 0, j = 0;
while (ch[j] != '\0')
{
c = ch[j] - 0x30; //查字符在库中位置
Oled_Data.vOled_Set_Address(x, y); //设置坐标
for (i = 0; i < 19; i++)
{
Oled_Data.vOled_Write_Byte(F19x24[c][i], 1);
}
Oled_Data.vOled_Set_Address(x, y + 1);
for (i = 19; i < 38; i++)
{
Oled_Data.vOled_Write_Byte(F19x24[c][i], 1);
}
Oled_Data.vOled_Set_Address(x, y + 2);
for (i = 38; i < 57; i++)
{
Oled_Data.vOled_Write_Byte(F19x24[c][i], 1);
}
x += 22; //下个字符的x坐标
j++; //下个字符
}
}
/*
* @description: 画横向的短线
* @param {uint8_t} x x坐标
* @param {uint8_t} y y坐标
* @param {uint8_t} num 需要的像素长度(左到右)
* @return {*} 无
* @Date: 2023-04-26 11:14:28
*/
// 画横向的短线
void vOled_Painting_Pixel(uint8_t x, uint8_t y, uint8_t num)
{
for(uint8_t i = 0; i <= num; i++)
{
Oled_Data.vOled_Set_Address(x + i, y);
Oled_Data.vOled_Write_Byte(0x01, OLED_DATA); // 写入0x01表示该像素点为白色(0xFF则表示8行像素即1列大小)
}
}
Bmp.c
// 略
AS608指纹
- 接线(看模块丝印,正负极不要接反!WAK引脚接不接无所谓看需要)
AS608 | STM32管脚 |
---|---|
3V3(红色) | 3V3 |
GND(黑色) | GND |
RXD(白色) | PB10(USART3_TX) |
TXD(黄色) | PB11(USART3_RX) |
WAK(蓝色) | PA15,有感应输出高电平,默认设置下拉输入即可 |
Vt(绿色) | 触摸感应电源输入端,接3.3V(这个不接的话WAK引脚没用!) |
- 需要用到一个定时器 TIM3进行串口接收时间计数
- 可以先把模块连接USB转串口模块然后打开上位机
SYDemo
进行测试录入等功能看看模块是否是好的- 一般是通过判断WAK引脚来看用户是否按下,还有就是那个模块的氛围灯好像关不了找手册也没看到,看着好不舒服,我想的是按下才亮,不按就灭…
- 程序编写
AS608.h
/*
*@Description: 指纹模块
*@Author: Yang
*@Date: 2023-04-21 15:16:12
*/
#ifndef __AS608_H
#define __AS608_H
#include "MyAll.h"
// AS608模块WAK引脚
#define AS608_WAK_PIN GPIO_PIN_15
// 特征存储位置1
#define AS608_SAVE_1 0x01
// 特征存储位置2
#define AS608_SAVE_2 0x02
// 指纹ID最大数(看手册0~299)
#define AS608_MAX_ID 299
// 指令-正确(0x00)
#define AS608_RETURN_TRUE 0x00
// 指令-未搜到指纹(0x09)
#define AS608_RETURN_NOT_FOUND 0x09
// 指令-处于一直把指纹放在上面状态
#define AS608_RETURN_ALWAY_LEAVE 0x17
// result值~处于一直把指纹放在上面状态
#define RESULT_ALWAY_LEAVE 3
// result值~默认值
#define RESULT_DEFAULT_VALUE 2
// 管理员指纹ID
#define ADMIN_ID 1
// LOG测试
#define AS608_LOG_1 0
// 开锁后倒计时时间(s)
#define AS608_OPEN_LOCK_TIME 6
typedef struct
{
uint16_t pageID;//指纹ID
uint16_t mathscore;//匹配得分
}SearchResult;
typedef struct
{
uint16_t PS_max;//指纹最大容量
uint8_t PS_level;//安全等级
uint32_t PS_addr;
uint8_t PS_size;//通讯数据包大小
uint8_t PS_N;//波特率基数N
}SysPara;
typedef struct
{
// 有效指纹个数
uint16_t AS608_Fingerprint_Number;
// 是否有指纹按下标志位(wak引脚)
bool AS608_Wak_Flag;
// 默认地址
uint32_t AS608_Addr;
uint8_t (*ucAS608_Get_Templete_Number)(uint16_t*);
uint8_t (*ucAS608_Admin_Brush_Fingerprint)(void);
void (*vAS608_Add_Fingerprint)(void);
void (*vAS608_Delete_Fingerprint)(void);
void (*vAS608_Search_Fingerprint_Number)(void);
void (*vAS608_Verify_Fingerprint_1)(void);
void (*vAS608_Fingerprint_Control_Function)(void);
void (*vAS608_Empty_Fingerprint_All)(void);
}AS608_TypeDef;
extern AS608_TypeDef AS608_Data;
uint8_t ucAS608_Admin_Brush_Fingerprint(void);
void vAS608_Add_Fingerprint(void);
void vAS608_Delete_Fingerprint(void);
void vAS608_Search_Fingerprint_Number(void);
void vAS608_Verify_Fingerprint_1(void);
void vAS608_Fingerprint_Control_Function(void);
void vAS608_Empty_Fingerprint_All(void);
uint8_t ucAS608_Get_Templete_Number(uint16_t *ValidN);
#endif
AS608.c
/*
*@Description: 指纹模块 接线:红色-->3.3 黑色-->GND 黄色-->USART3_RX 白色-->USART3_TX 蓝色-->PA15 绿色-->3.3V
*@Author: Yang
*@Date: 2023-04-21 15:07:12
*/
#include "As608.h"
/*====================================静态内部函数声明区 BEGIN====================================*/
static void svUsart3_Send_Byte(uint8_t byte_data);
static void svAS608_Send_Head(void);
static void svAS608_Send_Addr(void);
static void svAS608_Send_Flag(uint8_t flag);
static void svAS608_Send_Length(int length);
static void svAS608_Send_Cmd(uint8_t cmd);
static void svAS608_Send_Word(uint32_t word);
static void svAS608_Send_Check(uint16_t check);
static uint8_t sucAS608_Shake_Hand(uint32_t *PS_Addr);
static uint8_t *spucAS608_Wait_Str(uint16_t waittime);
static uint8_t sucAS608_Get_Image(void);
static uint8_t sucAS608_Create_Feature(uint8_t save_id);
static uint8_t sucAS608_Match(void);
static uint8_t sucAS608_Search(uint8_t save_id, uint16_t StartPage, uint16_t PageNum, SearchResult *p);
static uint8_t sucAS608_Compound_Feature(void);
static uint8_t sucAS608_Save_Template(uint8_t save_id, uint16_t PageID);
static uint8_t sucAS608_Delete_Template(uint16_t PageID, uint16_t N);
static uint8_t sucAS608_Empty_Template(void);
static uint8_t sucAS608_Write_Reg(uint8_t RegNum, uint8_t DATA);
static uint8_t sucAS608_Read_System_Parameters(SysPara *p);
static uint8_t sucAS608_Set_Addr(uint32_t PS_addr);
static uint8_t sucAS608_Write_Note(uint8_t NotePageNum, uint8_t *Byte32);
static uint8_t sucAS608_Read_Note(uint8_t NotePageNum, uint8_t *Byte32);
static uint8_t sucAS608_High_Speed_Search(uint8_t save_id, uint16_t StartPage, uint16_t PageNum, SearchResult *p);
static const char *cpcAS608_Error_Message(uint8_t ensure);
static uint8_t sucAS608_Verify_Fingerprint(SearchResult *p);
/*====================================静态内部函数声明区 END====================================*/
/*====================================变量区 BEGIN====================================*/
// 串口3接收数组
uint8_t Uart3_Rx_Buff[16];
AS608_TypeDef AS608_Data =
{
.AS608_Fingerprint_Number = 0,
.AS608_Wak_Flag = 0,
.AS608_Addr = 0xFFFFFFFF,
.ucAS608_Get_Templete_Number = &ucAS608_Get_Templete_Number,
.ucAS608_Admin_Brush_Fingerprint = &ucAS608_Admin_Brush_Fingerprint,
.vAS608_Add_Fingerprint = &vAS608_Add_Fingerprint,
.vAS608_Delete_Fingerprint = &vAS608_Delete_Fingerprint,
.vAS608_Search_Fingerprint_Number = &vAS608_Search_Fingerprint_Number,
.vAS608_Verify_Fingerprint_1 = &vAS608_Verify_Fingerprint_1,
.vAS608_Fingerprint_Control_Function = &vAS608_Fingerprint_Control_Function,
.vAS608_Empty_Fingerprint_All = &vAS608_Empty_Fingerprint_All
};
/*====================================变量区 END====================================*/
/*
* @description: 向串口3发送一个字节数据
* @param: 要发送的数据
* @return: 无
* @Date: 2023-04-22 09:00:58
*/
// 向串口3发送一个字节数据
static void svUsart3_Send_Byte(uint8_t byte_data)
{
// 循环等待USART3发送寄存器(SR)的第6位标志位(TXE)变为1,表示发送缓冲区为空,可以继续发送数据
while (0 == (USART3->SR & 0X40))
;
// 赋值给USART3数据寄存器(DR),从而实现向USART3发送数据
USART3->DR = byte_data;
}
/*
* @description: 向AS608发送包头
* @return: 无
* @Date: 2023-04-22 09:14:27
*/
// 向AS608发送包头
static void svAS608_Send_Head(void)
{
svUsart3_Send_Byte(0xEF);
svUsart3_Send_Byte(0x01);
}
/*
* @description: 向AS608发送芯片地址
* @return: 无
* @Date: 2023-04-22 09:20:40
*/
// 向AS608发送芯片地址
static void svAS608_Send_Addr(void)
{
svUsart3_Send_Byte((AS608_Data.AS608_Addr) >> 24);
svUsart3_Send_Byte((AS608_Data.AS608_Addr) >> 16);
svUsart3_Send_Byte((AS608_Data.AS608_Addr) >> 8);
svUsart3_Send_Byte(AS608_Data.AS608_Addr);
}
/*
* @description: 向AS608发送标识符
* @param: 标识符
* @return: 无
* @Date: 2023-04-22 09:22:24
*/
// 向AS608发送标识符
static void svAS608_Send_Flag(uint8_t flag)
{
svUsart3_Send_Byte(flag);
}
/*
* @description: 向AS608发送包长度
* @param: 包长度
* @return: 无
* @Date: 2023-04-22 09:24:40
*/
// 向AS608发送包长度
static void svAS608_Send_Length(int length)
{
svUsart3_Send_Byte(length >> 8);
svUsart3_Send_Byte(length);
}
/*
* @description: 向AS608发送命令码
* @param: 命令码
* @return: 无
* @Date: 2023-04-22 09:26:00
*/
// 向AS608发送命令码
static void svAS608_Send_Cmd(uint8_t cmd)
{
svUsart3_Send_Byte(cmd);
}
/*
* @description: 向AS608发送口令
* @param: 口令
* @return: 无
* @Date: 2023-04-22 09:28:19
*/
// 向AS608发送口令
static void svAS608_Send_Word(uint32_t word)
{
svUsart3_Send_Byte(word >> 24);
svUsart3_Send_Byte(word >> 16);
svUsart3_Send_Byte(word >> 8);
svUsart3_Send_Byte(word);
}
/*
* @description: 向AS608发送校验和
* @param: 校验和
* @return: 无
* @Date: 2023-04-22 09:30:06
*/
// 向AS608发送校验和
static void svAS608_Send_Check(uint16_t check)
{
svUsart3_Send_Byte(check >> 8);
svUsart3_Send_Byte(check);
}
/*
* @description: 与AS608握手
* @param: PS_Addr地址指针
* @return: 新的地址(正确地址)
* @Date: 2023-04-22 10:40:10
*/
// 与AS608握手
static uint8_t sucAS608_Shake_Hand(uint32_t *PS_Addr)
{
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址FFFFFFFF
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x0007); // 包长度:1(命令码1byte)+4(口令4byte)+2(校验码2byte)
svAS608_Send_Cmd(0x13); // 命令码
svAS608_Send_Word(0x00000000); // 口令:默认为00000000
svAS608_Send_Check(0x1b); // 校验码为01+07+13=1b
HAL_UART_Receive(&huart3, Uart3_Rx_Buff, 16, 100); // 等待回应
if ((0XEF == Uart3_Rx_Buff[0]) && (0X01 == Uart3_Rx_Buff[1]) && (0X07 == Uart3_Rx_Buff[6])) // 判断应答包
{
*PS_Addr = (Uart3_Rx_Buff[2] << 24) + (Uart3_Rx_Buff[3] << 16) + (Uart3_Rx_Buff[4] << 8) + (Uart3_Rx_Buff[5]); // 更新address
return 0;
}
return 1;
}
/*
* @description: 判断中断接收的数组有没有应答包
* @param: 等待中断接收数据的时间(单位1ms)
* @return: 数据包首地址
* @Date: 2023-04-22 10:37:32
*/
// 判断中断接收的数组有没有应答包
static uint8_t *spucAS608_Wait_Str(uint16_t waittime)
{
char *data;
uint8_t str[8];
str[0] = 0xef;
str[1] = 0x01;
str[2] = (AS608_Data.AS608_Addr) >> 24;
str[3] = (AS608_Data.AS608_Addr) >> 16;
str[4] = (AS608_Data.AS608_Addr) >> 8;
str[5] = AS608_Data.AS608_Addr;
str[6] = 0x07;
str[7] = '\0';
MyUSART3_Data.Usart3_Rx_Sta = 0;
while (--waittime)
{
HAL_Delay(1);
if (MyUSART3_Data.Usart3_Rx_Sta & 0x8000) // 接收到一次数据
{
data = strstr((const char *)MyUSART3_Data.Usart3_Rx_Buff, (const char *)str); // 比较接收到的包
if (data)
{
return (uint8_t *)data;
}
}
}
return 0;
}
/*
* @description: 录入图像(探测手指,探测到后录入指纹图像存于ImageBuffer)
* @return: 返回确认字
* @Date: 2023-04-22 10:51:47
*/
// 录入图像(探测手指,探测到后录入指纹图像存于ImageBuffer)
static uint8_t sucAS608_Get_Image(void)
{
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x03);
svAS608_Send_Cmd(0x01);
svAS608_Send_Check(0x0005);
data = spucAS608_Wait_Str(500); // 500
if (data)
{
ensure = data[9];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 生成特征(将ImageBuffer中的原始图像生成指纹特征文件存于AS608_SAVE_1或AS608_SAVE_2)
* @param: AS608_SAVE_1:0x01 AS608_SAVE_2:0x02
* @return: 返回确认字
* @Date: 2023-04-22 11:07:42
*/
// 生成特征
static uint8_t sucAS608_Create_Feature(uint8_t save_id)
{
uint16_t temp;
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x04);
svAS608_Send_Cmd(0x02);
svUsart3_Send_Byte(save_id);
temp = 0x01 + 0x04 + 0x02 + save_id;
svAS608_Send_Check(temp);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 精确比对两枚指纹特征(精确比对AS608_SAVE_1 与AS608_SAVE_2 中的特征文件)
* @return: 返回确认字
* @Date: 2023-04-22 11:15:05
*/
// 精确比对两枚指纹特征
static uint8_t sucAS608_Match(void)
{
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x03);
svAS608_Send_Cmd(0x03);
svAS608_Send_Check(0x0007);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 搜索指纹(以AS608_SAVE_1或AS608_SAVE_2 中的特征文件搜索整个或部分指纹库.若搜索到,则返回页码)
* @param: 特征存储位置1/2
* @param: 某一页开始
* @param: 到若干页
* @param: 搜索结果存储到结构体变量p中
* @return: 确认字,页码(相配指纹模板)
* @Date: 2023-04-22 11:19:46
*/
// 搜索指纹
static uint8_t sucAS608_Search(uint8_t save_id, uint16_t StartPage, uint16_t PageNum, SearchResult *p)
{
uint16_t temp;
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x08);
svAS608_Send_Cmd(0x04);
svUsart3_Send_Byte(save_id);
svUsart3_Send_Byte(StartPage >> 8); // 因为为2byte数据
svUsart3_Send_Byte(StartPage);
svUsart3_Send_Byte(PageNum >> 8);
svUsart3_Send_Byte(PageNum);
temp = 0x01 + 0x08 + 0x04 + save_id + (StartPage >> 8) + (uint8_t)StartPage + (PageNum >> 8) + (uint8_t)PageNum;
svAS608_Send_Check(temp);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
p->pageID = (data[10] << 8) + data[11];
p->mathscore = (data[12] << 8) + data[13];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 合并特征(生成模板)(将AS608_SAVE_1与AS608_SAVE_2中的特征文件合并生成 模板,结果存于AS608_SAVE_1与AS608_SAVE_2)
* @return: 返回确认字
* @Date: 2023-04-22 11:31:24
*/
// 生成模板
static uint8_t sucAS608_Compound_Feature(void)
{
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x03);
svAS608_Send_Cmd(0x05);
svAS608_Send_Check(0x0009);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 储存模板(将 AS608_SAVE_1 或 AS608_SAVE_2 中的模板文件存到 PageID 号flash数据库位置)
* @param: AS608_SAVE_1/AS608_SAVE_2
* @param: 指纹库位置号
* @return: 返回确认字
* @Date: 2023-04-22 11:36:52
*/
// 储存模板
static uint8_t sucAS608_Save_Template(uint8_t save_id, uint16_t PageID)
{
uint16_t temp;
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x06);
svAS608_Send_Cmd(0x06);
svUsart3_Send_Byte(save_id);
svUsart3_Send_Byte(PageID >> 8);
svUsart3_Send_Byte(PageID);
temp = 0x01 + 0x06 + 0x06 + save_id + (PageID >> 8) + (uint8_t)PageID;
svAS608_Send_Check(temp);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 删除模板(删除flash数据库中指定ID号开始的连续N个指纹模板)
* @param: 指纹库模板号
* @param: 删除的模板个数
* @return: 返回确认字
* @Date: 2023-04-22 11:41:24
*/
// 删除单个模板
static uint8_t sucAS608_Delete_Template(uint16_t PageID, uint16_t N)
{
uint16_t temp;
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x07);
svAS608_Send_Cmd(0x0C);
svUsart3_Send_Byte(PageID >> 8);
svUsart3_Send_Byte(PageID);
svUsart3_Send_Byte(N >> 8);
svUsart3_Send_Byte(N);
temp = 0x01 + 0x07 + 0x0C + (PageID >> 8) + (uint8_t)PageID + (N >> 8) + (uint8_t)N;
svAS608_Send_Check(temp);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 清空指纹库(删除flash数据库中所有指纹模板)
* @return: 返回确认字
* @Date: 2023-04-22 11:45:51
*/
// 清空指纹库(删除flash数据库中所有指纹模板)
static uint8_t sucAS608_Empty_Template(void)
{
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x03);
svAS608_Send_Cmd(0x0D);
svAS608_Send_Check(0x0011);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 写系统寄存器(写模块寄存器)
* @param: 寄存器序号RegNum:4\5\6
* @param: 数据
* @return: 返回确认字
* @Date: 2023-04-22 11:54:03
*/
// 写系统寄存器(写模块寄存器)
static uint8_t sucAS608_Write_Reg(uint8_t RegNum, uint8_t DATA)
{
uint16_t temp;
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x05);
svAS608_Send_Cmd(0x0E);
svUsart3_Send_Byte(RegNum);
svUsart3_Send_Byte(DATA);
temp = RegNum + DATA + 0x01 + 0x05 + 0x0E;
svAS608_Send_Check(temp);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 读系统基本参数(读取模块的基本参数(波特率,包大小等))
* @param: 结构体指针
* @return: 返回确认字 + 基本参数(16bytes)
* @Date: 2023-04-22 12:01:40
*/
// 读系统基本参数(读取模块的基本参数(波特率,包大小等))
static uint8_t sucAS608_Read_System_Parameters(SysPara *p)
{
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x03);
svAS608_Send_Cmd(0x0F);
svAS608_Send_Check(0x0013);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
p->PS_max = (data[14] << 8) + data[15]; // 模块最大指纹容量
p->PS_level = data[17]; // 对比等级
p->PS_addr = (data[18] << 24) + (data[19] << 16) + (data[20] << 8) + data[21]; // 地址
p->PS_size = data[23];
p->PS_N = data[25];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 设置模块地址
* @param: 地址
* @return: 返回确认字
* @Date: 2023-04-22 12:05:59
*/
// 设置模块地址
static uint8_t sucAS608_Set_Addr(uint32_t PS_addr)
{
uint16_t temp;
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x07);
svAS608_Send_Cmd(0x15);
svUsart3_Send_Byte(PS_addr >> 24);
svUsart3_Send_Byte(PS_addr >> 16);
svUsart3_Send_Byte(PS_addr >> 8);
svUsart3_Send_Byte(PS_addr);
temp = 0x01 + 0x07 + 0x15 + (uint8_t)(PS_addr >> 24) + (uint8_t)(PS_addr >> 16) + (uint8_t)(PS_addr >> 8) + (uint8_t)PS_addr;
svAS608_Send_Check(temp);
AS608_Data.AS608_Addr = PS_addr; // 发送完指令,更换地址
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
}
else
{
ensure = 0xFF;
}
AS608_Data.AS608_Addr = PS_addr;
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 写入记事本(模块内部为用户开辟了256bytes的FLASH空间用于存用户记事本该记事本逻辑上被分成 16 个页)
* @param: 0~15
* @param: 要写入的内容
* @return:返回确认字
* @Date: 2023-04-22 12:10:12
*/
// 写入记事本
static uint8_t sucAS608_Write_Note(uint8_t NotePageNum, uint8_t *Byte32)
{
uint16_t temp;
uint8_t ensure, i;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(36);
svAS608_Send_Cmd(0x18);
svUsart3_Send_Byte(NotePageNum);
for (i = 0; i < 32; i++)
{
svUsart3_Send_Byte(Byte32[i]);
temp += Byte32[i];
}
temp = 0x01 + 36 + 0x18 + NotePageNum + temp;
svAS608_Send_Check(temp);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 读记事(读取FLASH用户区的128bytes数据)
* @param: 0~15
* @param: 存储读到的数据
* @return: 返回确认字+用户信息
* @Date: 2023-04-22 12:14:35
*/
// 读记事
static uint8_t sucAS608_Read_Note(uint8_t NotePageNum, uint8_t *Byte32)
{
uint16_t temp;
uint8_t ensure, i;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x04);
svAS608_Send_Cmd(0x19);
svUsart3_Send_Byte(NotePageNum);
temp = 0x01 + 0x04 + 0x19 + NotePageNum;
svAS608_Send_Check(temp);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
for (i = 0; i < 32; i++)
{
Byte32[i] = data[10 + i];
}
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 高速搜索(以 AS608_SAVE_1 或 AS608_SAVE_2 中的特征文件高速搜索整个或部分指纹库。若搜索到,则返回页码,该指令对于的确存在于指纹库中 ,且登录时质量很好的指纹,会很快给出搜索结果。)
* @param: AS608_SAVE_1/AS608_SAVE_2
* @param: 起始页
* @param: 页数
* @param: 存储指针
* @return: 返回确认字+页码(相配指纹模板)
* @Date: 2023-04-22 12:18:37
*/
// 高速搜索指纹库
static uint8_t sucAS608_High_Speed_Search(uint8_t save_id, uint16_t StartPage, uint16_t PageNum, SearchResult *p)
{
uint16_t temp;
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x08);
svAS608_Send_Cmd(0x1b);
svUsart3_Send_Byte(save_id);
svUsart3_Send_Byte(StartPage >> 8);
svUsart3_Send_Byte(StartPage);
svUsart3_Send_Byte(PageNum >> 8);
svUsart3_Send_Byte(PageNum);
temp = 0x01 + 0x08 + 0x1b + save_id + (StartPage >> 8) + (uint8_t)StartPage + (PageNum >> 8) + (uint8_t)PageNum;
svAS608_Send_Check(temp);
data = spucAS608_Wait_Str(600);
if (data)
{
ensure = data[9];
p->pageID = (data[10] << 8) + data[11];
p->mathscore = (data[12] << 8) + data[13];
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 读有效模板个数
* @param: 存储读取结果
* @return: 返回确认字+有效模板个数ValidN
* @Date: 2023-04-22 12:24:55
*/
// 读有效模板个数(需要初始化时读取一次否则后面第一次读取是错误然后才正常)
uint8_t ucAS608_Get_Templete_Number(uint16_t *ValidN)
{
uint8_t ensure;
uint8_t *data;
svAS608_Send_Head(); // 包头EF01
svAS608_Send_Addr(); // 地址
svAS608_Send_Flag(0X01); // 包标识:命令
svAS608_Send_Length(0x03);
svAS608_Send_Cmd(0x1d);
svAS608_Send_Check(0x0021);
data = spucAS608_Wait_Str(500);
if (data)
{
ensure = data[9];
*ValidN = (data[10] << 8) + data[11]; // 有效指纹数
}
else
{
ensure = 0xFF;
}
memset(MyUSART3_Data.Usart3_Rx_Buff, 0, MyUSART3_Data.Usart3_Rx_Sta & 0x7FFF);
MyUSART3_Data.Usart3_Rx_Sta = 0;
return ensure;
}
/*
* @description: 各种错误信息输出
* @param: 错误编号
* @return: 字符串
* @Date: 2023-04-22 12:28:56
*/
// 各种错误信息输出
static const char *cpcAS608_Error_Message(uint8_t ensure)
{
const char *p;
switch (ensure)
{
case AS608_RETURN_TRUE:
{
p = "OK";
break;
}
case 0x01:
{
p = "数据包接收错误";
break;
}
case 0x02:
p = "传感器上没有手指";
break;
case 0x03:
{
p = "录入指纹图像失败";
break;
}
case 0x04:
{
p = "指纹图像太干、太淡而生不成特征";
break;
}
case 0x05:
{
p = "指纹图像太湿、太糊而生不成特征";
break;
}
case 0x06:
{
p = "指纹图像太乱而生不成特征";
break;
}
case 0x07:
{
p = "指纹图像正常,但特征点太少(或面积太小)而生不成特征";
break;
}
case 0x08:
{
p = "指纹不匹配";
break;
}
case AS608_RETURN_NOT_FOUND:
{
p = "没搜索到指纹";
break;
}
case 0x0a:
{
p = "特征合并失败";
break;
}
case 0x0b:
{
p = "访问指纹库时地址序号超出指纹库范围";
break;
}
case 0x10:
{
p = "删除模板失败";
break;
}
case 0x11:
{
p = "清空指纹库失败";
break;
}
case 0x15:
{
p = "缓冲区内没有有效原始图而生不成图像";
break;
}
case 0x18:
{
p = "读写 FLASH 出错";
break;
}
case 0x19:
{
p = "未定义错误";
break;
}
case 0x1a:
{
p = "无效寄存器号";
break;
}
case 0x1b:
{
p = "寄存器设定内容错误";
break;
}
case 0x1c:
{
p = "记事本页码指定错误";
break;
}
case 0x1f:
{
p = "指纹库满";
break;
}
case 0x20:
{
p = "地址错误";
break;
}
case AS608_RETURN_ALWAY_LEAVE:
{
p = "处于一直把指纹放在上面状态";
break;
}
default:
{
p = "模块返回确认码有误";
break;
}
}
return p;
}
/*******************************************应用层**************************************/
/*
* @description: 验证指纹
* @return {*} 验证成功-FINGERPRINT_TRUE 验证失败-FINGERPRINT_FALSE
* @Date: 2023-04-24 12:01:10
*/
// 验证指纹
static uint8_t sucAS608_Verify_Fingerprint(SearchResult *p)
{
uint8_t ensure;
uint8_t result;
while (1)
{
ensure = sucAS608_Get_Image();
if (AS608_RETURN_TRUE == ensure) // 获取图像成功
{
ensure = sucAS608_Create_Feature(AS608_SAVE_2);
if (AS608_RETURN_TRUE == ensure) // 生成特征成功
{
ensure = sucAS608_High_Speed_Search(AS608_SAVE_2, 0, AS608_MAX_ID, (SearchResult *)p);
#if AS608_LOG_1
printf("%d\r\n", ensure);
printf("%s\r\n", cpcAS608_Error_Message(ensure));
printf("R:%d---%d\r\n", p->pageID, p->mathscore);
#endif
if (AS608_RETURN_TRUE == ensure) // 搜索成功
{
result = FINGERPRINT_TRUE;
break;
}
else if (AS608_RETURN_NOT_FOUND == ensure) // 搜索不到
{
result = FINGERPRINT_FALSE;
break;
}
else if (AS608_RETURN_ALWAY_LEAVE == ensure)
{
result = RESULT_ALWAY_LEAVE;
break;
}
}
else
{
result = RESULT_DEFAULT_VALUE;
break;
}
}
else
{
result = RESULT_DEFAULT_VALUE;
break;
}
}
return result;
}
/*
* @description: 管理员刷指纹验证
* @return {*} 验证成功-FINGERPRINT_TRUE 验证失败-FINGERPRINT_FALSE
* @Date: 2023-04-24 10:58:37
*/
// 管理员刷指纹验证
uint8_t ucAS608_Admin_Brush_Fingerprint(void)
{
uint8_t result = RESULT_DEFAULT_VALUE;
SearchResult search;
result = sucAS608_Verify_Fingerprint(&search);
if (FINGERPRINT_TRUE == result) // 验证成功
{
if (Admin_Data.Admin_Fingerprint_ID == search.pageID) // 对比ID号
{
Buzzer_Data.vBuzzer_Ring();
// 回到主页面
printf("%d\r\n", search.pageID);
Menu_Data.Menu_State = INTERFACE_1;
Menu_Data.Menu_Flag_Buff[0] = 0;
Admin_Data.Administrator_Flag = 1;
}
// printf("L:%d---%d\r\n",search.pageID,search.mathscore);
}
return result;
}
/*
* @description: 添加指纹,存储到指定ID
* @return {*}
* @Date: 2023-04-24 15:02:54
*/
// 添加指纹,存储到指定ID
void vAS608_Add_Fingerprint(void)
{
uint8_t ensure;
// 执行步骤索引
uint8_t Add_Index = 1;
// 可选择ID范围0~299(但是一般不要用0,1~299即可)
uint16_t id = 1;
uint8_t arr[20];
Oled_Data.vOled_Clear();
Oled_Data.vOled_Light_Row(5, SET); // 显示一行像素
Oled_Data.vOled_Display_Gb2312_String(2, 6, (uint8_t *)"返回");
// 当录指纹成功或者按返回才会回到上一级菜单
while (1)
{
switch (Add_Index)
{
case 1:
{
// 显示文字
Oled_Data.vOled_Display_Gb2312_String(27, 2, (uint8_t *)"请按指纹");
ensure = sucAS608_Get_Image();
if (AS608_RETURN_TRUE == ensure) // 等待模块发回指令
{
Buzzer_Data.vBuzzer_Ring();
HAL_Delay(100); // 这里需要延时一下,模块内部处理图像需要时间
ensure = sucAS608_Create_Feature(AS608_SAVE_1);
if (AS608_RETURN_TRUE == ensure)
{
Oled_Data.vOled_Display_Gb2312_String(27, 2, (uint8_t *)"指纹正常");
HAL_Delay(MESSAGE_TIME);
Add_Index++; // 跳到下一步
}
else
{
printf("%s\r\n", cpcAS608_Error_Message(ensure));
}
}
else
{
printf("%s\r\n", cpcAS608_Error_Message(ensure));
}
break;
}
case 2:
{
Oled_Data.vOled_Display_Gb2312_String(27, 2, (uint8_t *)"再按一次");
ensure = sucAS608_Get_Image();
if (AS608_RETURN_TRUE == ensure) // 等待模块发回指令
{
Buzzer_Data.vBuzzer_Ring();
HAL_Delay(100); // 这里需要延时一下,模块内部处理图像需要时间
ensure = sucAS608_Create_Feature(AS608_SAVE_2);
if (AS608_RETURN_TRUE == ensure)
{
Oled_Data.vOled_Display_Gb2312_String(27, 2, (uint8_t *)"指纹正常");
HAL_Delay(MESSAGE_TIME);
Add_Index++; // 跳到下一步
}
else
{
printf("%s\r\n", cpcAS608_Error_Message(ensure));
Add_Index = 1;
}
}
else
{
printf("%s\r\n", cpcAS608_Error_Message(ensure));
}
break;
}
case 3:
{
Oled_Data.vOled_Display_Gb2312_String(27, 2, (uint8_t *)"对比指纹");
HAL_Delay(MESSAGE_TIME);
ensure = sucAS608_Match();
if (AS608_RETURN_TRUE == ensure) // 对比成功
{
Oled_Data.vOled_Display_Gb2312_String(27, 2, (uint8_t *)"对比成功");
HAL_Delay(MESSAGE_TIME);
Add_Index++; // 跳到下一步
}
else // 对比失败
{
Oled_Data.vOled_Display_Gb2312_String(27, 2, (uint8_t *)"对比失败");
HAL_Delay(MESSAGE_TIME);
Add_Index = 1; // 回到第一步
}
break;
}
case 4:
{
Oled_Data.vOled_Display_Gb2312_String(27, 2, (uint8_t *)"生成模板");
HAL_Delay(MESSAGE_TIME);
ensure = sucAS608_Compound_Feature();
if (AS608_RETURN_TRUE == ensure) // 生成模板成功
{
Oled_Data.vOled_Display_Gb2312_String(27, 2, (uint8_t *)"生成成功");
HAL_Delay(MESSAGE_TIME);
Add_Index++; // 跳到下一步
}
else
{
Oled_Data.vOled_Display_Gb2312_String(27, 2, (uint8_t *)"生成失败");
HAL_Delay(MESSAGE_TIME);
Add_Index = 1; // 回到第一步
}
break;
}
case 5: // 选择存储ID
{
Oled_Data.vOled_Display_Gb2312_String(2, 6, (uint8_t *)"返回");
Oled_Data.vOled_Display_Gb2312_String(47, 6, (uint8_t *)"加减");
Oled_Data.vOled_Display_Gb2312_String(96, 6, (uint8_t *)"确认");
if (Key_Data.Key_Down_Buff[1]) // K2短按++
{
Key_Data.Key_Down_Buff[1] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
id++;
if (id > 299)
{
id = 1;
}
Oled_Data.vOled_Clear();
}
else if (Key_Data.Key_Down_Buff[4]) // K2长按
{
Key_Data.Key_Down_Buff[4] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
if (id > 1)
{
id--;
}
Oled_Data.vOled_Clear();
}
else if (Key_Data.Key_Down_Buff[2]) // K3短按确认
{
Key_Data.Key_Down_Buff[2] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
ensure = sucAS608_Save_Template(AS608_SAVE_2, id); // 把第二次按下的存储
if (AS608_RETURN_TRUE == ensure)
{
Oled_Data.vOled_Clear();
snprintf(arr, sizeof(arr), "添加成功,ID为%d", id);
Oled_Data.vOled_Display_Gb2312_String(10, 2, (uint8_t *)arr);
HAL_Delay(MESSAGE_TIME);
Menu_Data.Menu_State = INTERFACE_3;
Menu_Data.Menu_Flag_Buff[2] = 1;
return;
}
else
{
Oled_Data.vOled_Clear();
Oled_Data.vOled_Display_Gb2312_String(27, 2, (uint8_t *)"添加失败");
HAL_Delay(MESSAGE_TIME);
Add_Index = 1; // 回到第一步
}
}
snprintf(arr, sizeof(arr), "选择存储ID为:%d", id);
Oled_Data.vOled_Display_Gb2312_String(0, 2, (uint8_t *)arr);
break;
}
}
// 回到功能页面
if (Key_Data.Key_Down_Buff[0])
{
Key_Data.Key_Down_Buff[0] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
Menu_Data.Menu_State = INTERFACE_3;
Menu_Data.Menu_Flag_Buff[2] = 1;
return;
}
MyUSART1_Data.vUsart1_Rx_Data_Analytic(); // 不要忘记串口数据解析(因为这是死循环所以不放这接收不到)
}
}
/*
* @description: 指定ID删除单个指纹
* @return {*}
* @Date: 2023-04-24 18:05:44
*/
// 指定ID删除单个指纹
void vAS608_Delete_Fingerprint(void)
{
uint8_t ensure;
uint8_t Add_Index = 1;
// 可选择ID范围0~299
uint16_t id = 0;
uint8_t arr[30];
Oled_Data.vOled_Clear();
while (1)
{
switch (Add_Index)
{
case 1:
{
Oled_Data.vOled_Light_Row(5, SET); // 显示一行像素
snprintf(arr, sizeof(arr), "要删除的ID为:%d", id);
Oled_Data.vOled_Display_Gb2312_String(0, 2, (uint8_t *)arr);
Oled_Data.vOled_Display_Gb2312_String(2, 6, (uint8_t *)"返回");
Oled_Data.vOled_Display_Gb2312_String(47, 6, (uint8_t *)"加减");
Oled_Data.vOled_Display_Gb2312_String(96, 6, (uint8_t *)"确认");
if (Key_Data.Key_Down_Buff[1])
{
Key_Data.Key_Down_Buff[1] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
id++;
if (id >= 299)
{
id = 299;
}
Oled_Data.vOled_Clear();
}
if (Key_Data.Key_Down_Buff[4])
{
Key_Data.Key_Down_Buff[4] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
if (id != 0)
{
id--;
}
Oled_Data.vOled_Clear();
}
if (Key_Data.Key_Down_Buff[2]) // 确认
{
Key_Data.Key_Down_Buff[2] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
snprintf(arr, sizeof(arr), "确认要删除id%d吗?", id);
Oled_Data.vOled_Clear();
Oled_Data.vOled_Display_Gb2312_String(0, 2, (uint8_t *)arr);
Oled_Data.vOled_Display_Gb2312_String(2, 6, (uint8_t *)"取消");
Oled_Data.vOled_Display_Gb2312_String(96, 6, (uint8_t *)"确认");
Add_Index++;
}
break;
}
case 2:
{
if (Key_Data.Key_Down_Buff[0]) // 取消
{
Key_Data.Key_Down_Buff[0] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
id = 0;
Oled_Data.vOled_Clear();
Add_Index = 1;
}
if (Key_Data.Key_Down_Buff[2]) // 确认
{
Key_Data.Key_Down_Buff[2] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
ensure = sucAS608_Delete_Template(id, 1);
if (AS608_RETURN_TRUE == ensure)
{
Oled_Data.vOled_Clear();
Oled_Data.vOled_Light_Row(5, SET); // 显示一行像素
snprintf(arr, sizeof(arr), "删除ID%d成功", id);
Oled_Data.vOled_Display_Gb2312_String(0, 2, (uint8_t *)arr);
HAL_Delay(MESSAGE_TIME);
Menu_Data.Menu_State = INTERFACE_3;
Menu_Data.Menu_Flag_Buff[2] = 1;
return;
}
else
{
Oled_Data.vOled_Clear();
Oled_Data.vOled_Light_Row(5, SET); // 显示一行像素
memset(arr, 0, sizeof(arr));
snprintf(arr, sizeof(arr), "删除ID%d失败", id);
Oled_Data.vOled_Display_Gb2312_String(0, 2, (uint8_t *)arr);
HAL_Delay(MESSAGE_TIME);
Oled_Data.vOled_Clear();
Add_Index = 1;
}
}
break;
}
}
// 回到功能页面
if (Key_Data.Key_Down_Buff[0])
{
Key_Data.Key_Down_Buff[0] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
Menu_Data.Menu_State = INTERFACE_3;
Menu_Data.Menu_Flag_Buff[2] = 1;
return;
}
MyUSART1_Data.vUsart1_Rx_Data_Analytic(); // 不要忘记串口数据解析(因为这是死循环所以不放这接收不到)
}
}
/*
* @description: 指纹数量
* @return {*}
* @Date: 2023-04-24 19:24:54
*/
// 指纹数量
void vAS608_Search_Fingerprint_Number(void)
{
uint8_t arr[20];
uint8_t ensure;
Oled_Data.vOled_Clear();
Oled_Data.vOled_Light_Row(5, SET); // 显示一行像素
Oled_Data.vOled_Display_Gb2312_String(2, 6, (uint8_t *)"返回");
ensure = AS608_Data.ucAS608_Get_Templete_Number(&AS608_Data.AS608_Fingerprint_Number);
while (1)
{
if (AS608_RETURN_TRUE == ensure)
{
snprintf(arr, sizeof(arr), "已用ID:%d 个", AS608_Data.AS608_Fingerprint_Number);
Oled_Data.vOled_Display_Gb2312_String(10, 0, (uint8_t *)arr);
snprintf(arr, sizeof(arr), "剩余ID:%d 个", AS608_MAX_ID - AS608_Data.AS608_Fingerprint_Number);
Oled_Data.vOled_Display_Gb2312_String(10, 2, (uint8_t *)arr);
}
else
{
Oled_Data.vOled_Display_Gb2312_String(10, 1, (uint8_t *)"查询失败!!!");
HAL_Delay(MESSAGE_TIME);
Menu_Data.Menu_State = INTERFACE_3;
Menu_Data.Menu_Flag_Buff[2] = 1;
}
// 回到功能页面
if (Key_Data.Key_Down_Buff[0])
{
Key_Data.Key_Down_Buff[0] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
Menu_Data.Menu_State = INTERFACE_3;
Menu_Data.Menu_Flag_Buff[2] = 1;
return;
}
if (INTERFACE_6 == Menu_Data.Menu_State)
{
return;
}
MyUSART1_Data.vUsart1_Rx_Data_Analytic(); // 串口通信
}
}
/*
* @description: 验证指纹---功能区
* @return {*} 无
* @Date: 2023-04-26 13:00:23
*/
// 验证指纹---功能区
void vAS608_Verify_Fingerprint_1(void)
{
uint8_t v_Flag = 0;
SearchResult search;
uint8_t result = RESULT_DEFAULT_VALUE;
uint8_t arr[20];
while (1)
{
MyUSART1_Data.vUsart1_Rx_Data_Analytic(); // 串口通信
// 回到功能页面
if (Key_Data.Key_Down_Buff[0])
{
Key_Data.Key_Down_Buff[0] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
Menu_Data.Menu_State = INTERFACE_3;
Menu_Data.Menu_Flag_Buff[2] = 1;
return;
}
if (0 == v_Flag)
{
v_Flag = 1;
Menu_Data.vMenu_Admin_Verify_Interface();
}
result = sucAS608_Verify_Fingerprint(&search);
MyAll_Data.vTime_Out_Init(); // 超时等待清0
if (FINGERPRINT_TRUE == result) // 验证成功
{
Buzzer_Data.vBuzzer_Ring();
Oled_Data.vOled_Clear();
snprintf(arr, sizeof(arr), "验证成功,ID为%d", search.pageID);
Oled_Data.vOled_Display_Gb2312_String(0, 2, (uint8_t *)arr);
HAL_Delay(MESSAGE_TIME);
v_Flag = 0;
result = RESULT_DEFAULT_VALUE;
}
else if (FINGERPRINT_FALSE == result)
{
Buzzer_Data.vBuzzer_Ring();
Oled_Data.vOled_Clear();
snprintf(arr, sizeof(arr), "验证失败!!!");
Oled_Data.vOled_Display_Gb2312_String(10, 2, (uint8_t *)arr);
HAL_Delay(MESSAGE_TIME);
// 回到主页面
v_Flag = 0;
result = RESULT_DEFAULT_VALUE;
}
else if (RESULT_ALWAY_LEAVE == result)
{
Buzzer_Data.vBuzzer_Ring();
Oled_Data.vOled_Clear();
snprintf(arr, sizeof(arr), "勿一直放传感器上");
Oled_Data.vOled_Display_Gb2312_String(0, 2, (uint8_t *)arr);
HAL_Delay(MESSAGE_TIME);
v_Flag = 0;
result = RESULT_DEFAULT_VALUE;
}
}
}
/*
* @description: 指纹控制函数
* @return {*}
* @Date: 2023-04-26 18:30:45
*/
// 指纹控制函数
void vAS608_Fingerprint_Control_Function(void)
{
uint8_t arr[20];
SearchResult search;
uint8_t result = RESULT_DEFAULT_VALUE;
result = sucAS608_Verify_Fingerprint(&search);
MyAll_Data.vTime_Out_Init(); // 超时等待清0
if (FINGERPRINT_TRUE == result) // 验证成功
{
SG90_Data.vSG90_Mode_switch();
result = RESULT_DEFAULT_VALUE;
}
else if (FINGERPRINT_FALSE == result)
{
Buzzer_Data.vBuzzer_Ring();
Oled_Data.vOled_Clear();
snprintf(arr, sizeof(arr), "验证失败!!!");
Oled_Data.vOled_Display_Gb2312_String(20, 2, (uint8_t *)arr);
HAL_Delay(MESSAGE_TIME);
result = RESULT_DEFAULT_VALUE;
}
}
/*
* @description: 清空指纹库界面 【功能】
* @return {*}
* @Date: 2023-04-27 14:46:13
*/
// 清空指纹库界面 【功能】
void vAS608_Empty_Fingerprint_All(void)
{
uint8_t arr[20];
uint8_t ensure;
Oled_Data.vOled_Clear();
Oled_Data.vOled_Light_Row(5, SET); // 显示一行像素
Oled_Data.vOled_Display_Gb2312_String(0, 0, (uint8_t *)"是否真的要清空?");
Oled_Data.vOled_Display_Gb2312_String(2, 6, (uint8_t *)"取消");
Oled_Data.vOled_Display_Gb2312_String(96, 6, (uint8_t *)"确认");
while (1)
{
// 取消清空 回到功能页面
if (Key_Data.Key_Down_Buff[0])
{
Key_Data.Key_Down_Buff[0] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
Menu_Data.Menu_State = INTERFACE_3;
Menu_Data.Menu_Flag_Buff[2] = 1;
return;
}
// 删除!!!
if (Key_Data.Key_Down_Buff[2])
{
Key_Data.Key_Down_Buff[2] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
ensure = sucAS608_Empty_Template();
if (AS608_RETURN_TRUE == ensure)
{
Oled_Data.vOled_Display_Gb2312_String(30, 0, (uint8_t *)"已清空");
HAL_Delay(MESSAGE_TIME);
Menu_Data.Menu_State = INTERFACE_3;
Menu_Data.Menu_Flag_Buff[2] = 1;
return;
}
else
{
Oled_Data.vOled_Display_Gb2312_String(10, 1, (uint8_t *)"清空失败!!!");
HAL_Delay(MESSAGE_TIME);
Menu_Data.Menu_State = INTERFACE_3;
Menu_Data.Menu_Flag_Buff[2] = 1;
return;
}
}
if (INTERFACE_6 == Menu_Data.Menu_State)
{
return;
}
MyUSART1_Data.vUsart1_Rx_Data_Analytic(); // 串口通信
}
}
SG90
- 接线
SG90 | STM32管脚 |
---|---|
VCC(红色) | 5V |
GND(棕色) | GND |
PWM线(橙色) | PB8(TIM4_CH3) |
需要在MX设置PWM频率为50HZ
- 程序编写
SG90.h
#ifndef __SG90_H
#define __SG90_H
#include "MyAll.h"
// SG90定时器的arr计数值
#define SG90_ARR_VALUE 200
// SG90--0度所需占空比
#define SG90_ANGLE_0 2.5
// SG90--45度所需占空比
#define SG90_ANGLE_45 5
// SG90--90度所需占空比
#define SG90_ANGLE_90 7.5
// SG90--180度所需占空比
#define SG90_ANGLE_180 12.5
typedef struct
{
void (*vSG90_Set_Duty)(float);
void (*vSG90_Control_Angle)(uint8_t);
void (*vSG90_Mode_switch)(void);
} SG90_TypeDef;
extern SG90_TypeDef SG90_Data;
void vSG90_Set_Duty(float duty);
void vSG90_Control_Angle(uint8_t angle);
void vSG90_Mode_switch(void);
#endif
SG90.c
/*
*@Description: SG90舵机
*@Author: Yang
*@Date: 2023-04-26 14:46:43
*/
#include "SG90.h"
/*====================================变量区 BEGIN====================================*/
SG90_TypeDef SG90_Data =
{
.vSG90_Set_Duty = &vSG90_Set_Duty,
.vSG90_Control_Angle = &vSG90_Control_Angle,
.vSG90_Mode_switch = &vSG90_Mode_switch
};
/*====================================变量区 END====================================*/
/*
* @description: 设置SG90占空比
* @param {float} duty 占空比范围(0~100)
* @return {*}
* @Date: 2023-04-26 15:18:03
*/
// 设置SG90占空比
void vSG90_Set_Duty(float duty)
{
TIM4->CCR3 = SG90_ARR_VALUE * (duty / 100.0f); // 设置占空比
}
/*
* @description: 控制舵机转动0~180度(通过斜率公式计算)
* @param {uint8_t} angle 范围0~180
* @return {*} 无
* @Date: 2023-04-26 17:38:07
*/
// 控制舵机转动0~180度
void vSG90_Control_Angle(uint8_t angle)
{
float duty_temp = 0;
duty_temp = (0.055f * angle) + 2.55;
SG90_Data.vSG90_Set_Duty(duty_temp);
}
/*
* @description: 舵机开关门动作及显示函数
* @return {*}
* @Date: 2023-05-02 19:08:32
*/
// 舵机开关门动作及显示函数
void vSG90_Mode_switch(void)
{
uint8_t arr[20];
Oled_Data.vOled_Clear();
Buzzer_Data.vBuzzer_Ring();
SG90_Data.vSG90_Control_Angle(180); // 开锁
Oled_Data.vOled_Light_Row(1, SET); // 显示一行像素
Oled_Data.vOled_Light_Row(4, SET); // 显示一行像素
Oled_Data.vOled_Display_Gb2312_String(35, 2, (uint8_t *)"已开锁");
MyUSART2_Data.WIFI_printf("Open\r\n");
for (uint8_t i = AS608_OPEN_LOCK_TIME; i > 0; i--)
{
MyAll_Data.vTime_Out_Init(); // 超时等待清0
snprintf(arr, sizeof(arr), "将在%02d秒后上锁", i);
Oled_Data.vOled_Display_Gb2312_String(0, 6, (uint8_t *)arr);
HAL_Delay(1000);
}
Oled_Data.vOled_Clear();
SG90_Data.vSG90_Control_Angle(0); // 上锁
Oled_Data.vOled_Light_Row(1, SET); // 显示一行像素
Oled_Data.vOled_Light_Row(4, SET); // 显示一行像素
Oled_Data.vOled_Display_Gb2312_String(35, 2, (uint8_t *)"已上锁");
HAL_Delay(MESSAGE_TIME);
}
通用
- 程序编写
MyAll.h
/*
*@Description: 总头文件
*@Author: Yang
*@Date: 2023-04-21 16:19:37
*/
#ifndef __MYALL_H
#define __MYALL_H
#include "main.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "rtc.h"
#include "iwdg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>
#include <stdarg.h>
#include "As608.h"
#include "Led.h"
#include "Key.h"
#include "Oled.h"
#include "Buzzer.h"
#include "MyUSART1.h"
#include "MyUSART2.h"
#include "MyUSART3.h"
#include "Menu.h"
#include "Clock.h"
#include "SG90.h"
#include "ESP8266.h"
// 按键扫描(单位:ms)
#define KEY_TASK_TIME 10
// LED间隔闪烁(单位:ms)
#define LED_TASK_TIME 300
// 喂狗间隔(单位: ms)MX设置是6s
#define IWDG_TIME 5800
// 打印测试
#define MY_ALL_LOG_1 0
// 打印测试
#define MENU_LOG_1 0
typedef struct
{
void (*vHardware_Init)(void);
uint8_t (*vPassword_Contrast)(uint8_t *, uint8_t *);
void (*vTime_Out_Init)(void);
} MyAll_TypeDef;
extern MyAll_TypeDef MyAll_Data;
void vHardware_Init(void);
uint8_t vPassword_Contrast(uint8_t *Old_str, uint8_t *New_str);
void vTime_Out_Init(void);
#endif
MyAll.c
/*
*@Description: 通用
*@Author: Yang
*@Date: 2023-04-21 16:22:56
*/
#include "MyAll.h"
/*====================================变量区 BEGIN====================================*/
MyAll_TypeDef MyAll_Data =
{
.vHardware_Init = &vHardware_Init,
.vPassword_Contrast = &vPassword_Contrast,
.vTime_Out_Init = &vTime_Out_Init
};
// RTC时间结构体
RTC_TimeTypeDef rtc_Time = {0};
// RTC日期结构体
RTC_DateTypeDef rtc_Date = {0};
/*====================================变量区 END====================================*/
/*
* @description: 外设初始化
* @return: 无
* @Date: 2023-04-21 17:03:30
*/
// 外设初始化
void vHardware_Init(void)
{
printf("KEY1:模拟按键1\r\n");
printf("KEY2:模拟按键2\r\n");
printf("KEY3:模拟按键3\r\n");
printf("KEY2_L:模拟按键2长按\r\n");
printf("ROOT:管理员身份\r\n");
TIM3->CNT = 0;
HAL_TIM_Base_Stop_IT(&htim3);
HAL_UART_Receive_IT(&huart3, (uint8_t *)&MyUSART3_Data.Usart3_New_Data, 1);
Oled_Data.vOled_Init();
Oled_Data.vOled_Toggle_Display(OLED_SET);
Oled_Data.vOled_Rotate_Display(OLED_SET);
AS608_Data.ucAS608_Get_Templete_Number(&AS608_Data.AS608_Fingerprint_Number); // 获取一次指纹数量
Menu_Data.vMenu_Start_Interface();
__HAL_RCC_RTC_ENABLE(); // 使能RTC
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_3); // 使能PWM通道
SG90_Data.vSG90_Control_Angle(0); //初始化舵机0度
Clock_Data.vClock_Get_Time_Date(&rtc_Time); //获取时间日期
ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_RESTORE, ESP8266_RETURN_OK, ESP8266_WAIT_TIME);
}
/*
* @description: 中断服务函数(需要去删除stm32f1xx_it.c里的)
* @return: 无
* @Date: 2023-04-22 00:04:33
*/
void SysTick_Handler(void)
{
// LED计数
static uint16_t Led_Timer_Cnt = 0;
// 按键长按计数
static uint8_t Key_Timer_Cnt = 0;
// 按键按下计数
static uint8_t Key_Down_Cnt = 0;
// 蜂鸣器响计数
static uint8_t Buzzer_Set_Timer_Cnt = 0;
// 太空人刷新计数
static uint8_t Bmp_Space_Person_Cnt = 0;
// 时钟计数
static uint16_t Standby_Cnt = 0;
// 看门狗计数
static uint16_t IWDG_Cnt = 0;
HAL_IncTick();
Key_Down_Cnt++;
Led_Timer_Cnt++;
Key_Timer_Cnt++;
Bmp_Space_Person_Cnt++;
Standby_Cnt++;
IWDG_Cnt++;
if(IWDG_TIME == IWDG_Cnt)
{
IWDG_Cnt = 0;
HAL_IWDG_Refresh(&hiwdg); // 喂狗
}
if (1000 == Standby_Cnt)
{
Standby_Cnt = 0;
Clock_Data.vClock_Get_Time_Date(&rtc_Time); // 时间赋值
Menu_Data.Time_Out_Standby_Count++;
if ((TIME_OUT_MAX_TIME == Menu_Data.Time_Out_Standby_Count) && (1 == Menu_Data.Standby_Choose_Flag))
{
Menu_Data.Menu_Flag_Buff[5] = 1;
Menu_Data.Menu_State = INTERFACE_6;
}
}
if (130 == Bmp_Space_Person_Cnt)
{
Bmp_Space_Person_Cnt = 0;
Menu_Data.Bmp_Space_Person_Flag = 1;
}
if (Buzzer_Data.Buzzer_Open_Flag)
{
Buzzer_Data.vBuzzer_Control(BUZZER_ON);
Buzzer_Set_Timer_Cnt++;
if (BUZZER_TIME == Buzzer_Set_Timer_Cnt)
{
Buzzer_Set_Timer_Cnt = 0;
Buzzer_Data.vBuzzer_Control(BUZZER_OFF);
Buzzer_Data.Buzzer_Open_Flag = 0;
}
}
if (100 == Key_Down_Cnt)
{
Key_Down_Cnt = 0;
Key_Data.Key_Down_Time++;
}
if (LED_TASK_TIME == Led_Timer_Cnt)
{
Led_Timer_Cnt = 0;
Led_Data.vLed_Flashing();
}
if (KEY_TASK_TIME == Key_Timer_Cnt)
{
Key_Timer_Cnt = 0;
Key_Data.vKey_Scan_Function();
if (SET == HAL_GPIO_ReadPin(GPIOA, AS608_WAK_PIN))
{
if (SET == HAL_GPIO_ReadPin(GPIOA, AS608_WAK_PIN))
{
AS608_Data.AS608_Wak_Flag = 1;
}
}
}
}
/*
* @description: 定时器中断回调函数
* @param {TIM_HandleTypeDef} *htim
* @return {*}
* @Date: 2023-04-24 02:01:50
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(&htim3 == htim)
{
MyUSART3_Data.Usart3_Rx_Sta |= 1 << 15;
HAL_TIM_Base_Stop_IT(&htim3);
}
}
/*
* @description: 管理员密码对比
* @param {uint8_t} Old_str 旧
* @param {uint8_t} New_str 输入的
* @return: 正确---PASSWORD_TRUE 错误---PASSWORD_FALSE
* @Date: 2023-04-24 02:06:19
*/
// 管理员密码对比
uint8_t vPassword_Contrast(uint8_t *Old_str, uint8_t *New_str)
{
uint8_t result = PASSWORD_TRUE;
for (uint8_t i = 0; i < 3; i++)
{
if (Old_str[i] != New_str[i])
{
result = PASSWORD_FALSE;
}
}
#if MY_ALL_LOG_1
printf("Password:%d\r\n", result);
#endif
return result;
}
/*
* @description: 超时等待时间清0
* @return {*}
* @Date: 2023-04-25 05:46:55
*/
// 超时等待时间清0
void vTime_Out_Init(void)
{
Menu_Data.Time_Out_Standby_Count = 0;
}
串口
- 程序编写
MyUSART1.h
/*
*@Description: 串口
*@Author: Yang
*@Date: 2023-04-21 19:02:50
*/
#ifndef __MYUSART1_H
#define __MYUSART1_H
#include "MyAll.h"
// USART1接收的最大长度数据
#define USART1_MAX_LEN 50
typedef struct
{
// USART1接收数组
uint8_t Usart1_Rx_Buff[USART1_MAX_LEN];
// USART1接收长度
uint8_t Usart1_Rx_Len;
// USART1接收完成标志位
bool Usart1_Rx_Over_Flag;
void (*vUsart1_Rx_Data_Analytic)(void);
} MyUSART1_TypeDef;
extern MyUSART1_TypeDef MyUSART1_Data;
void vUsart1_Rx_Data_Analytic(void);
#endif
MyUSART1.c
/*
*@Description: 串口1--9600bound 串口3--57600bound
*@Author: Yang
*@Date: 2023-04-21 19:02:39
*/
#include "MyUSART1.h"
/*====================================变量区 BEGIN====================================*/
MyUSART1_TypeDef MyUSART1_Data =
{
.Usart1_Rx_Buff = {0},
.Usart1_Rx_Len = 0,
.Usart1_Rx_Over_Flag = 0,
.vUsart1_Rx_Data_Analytic = &vUsart1_Rx_Data_Analytic
};
extern DMA_HandleTypeDef hdma_usart1_rx;
/*====================================变量区 END====================================*/
/*
* @description: USART1数据解析
* @return: 无
* @Date: 2023-04-22 00:45:56
*/
// USART1数据解析
void vUsart1_Rx_Data_Analytic(void)
{
if (MyUSART1_Data.Usart1_Rx_Over_Flag)
{
MyUSART1_Data.Usart1_Rx_Over_Flag = 0;
if ((MyUSART1_Data.Usart1_Rx_Len > 1) && Menu_Data.Menu_Usart1_State) // 需要打开通信
{
// 比较
if(0 == strncmp(MyUSART1_Data.Usart1_Rx_Buff, "KEY1", 4))
{
Key_Data.Key_Down_Buff[0] = 1;
}
// 比较
else if(0 == strncmp(MyUSART1_Data.Usart1_Rx_Buff, "KEY2", 4))
{
if(4 == MyUSART1_Data.Usart1_Rx_Len)
{
Key_Data.Key_Down_Buff[1] = 1;
}
}
// 比较
else if(0 == strncmp(MyUSART1_Data.Usart1_Rx_Buff, "KEY3", 4))
{
Key_Data.Key_Down_Buff[2] = 1;
}
// 比较
else if(0 == strncmp(MyUSART1_Data.Usart1_Rx_Buff, "KEY2_L", 6))
{
if(6 == MyUSART1_Data.Usart1_Rx_Len)
{
Key_Data.Key_Down_Buff[4] = 1;
}
}
// 比较
else if(0 == strncmp(MyUSART1_Data.Usart1_Rx_Buff, "ROOT", 4))
{
if(4 == MyUSART1_Data.Usart1_Rx_Len)
{
Admin_Data.Administrator_Flag = 1;
}
}
MyAll_Data.vTime_Out_Init(); //超时等待清0
}
HAL_UART_Transmit(&huart1, (uint8_t *)MyUSART1_Data.Usart1_Rx_Buff, strlen(MyUSART1_Data.Usart1_Rx_Buff), 0xffff);
memset(MyUSART1_Data.Usart1_Rx_Buff, 0, sizeof(MyUSART1_Data.Usart1_Rx_Buff));
// 重新打开DMA接收
HAL_UART_Receive_DMA(&huart1, (uint8_t *)MyUSART1_Data.Usart1_Rx_Buff, USART1_MAX_LEN);
}
}
/*
* @description: USART1中断函数(需要在stm32f1xx_it.c删除)
* @return: 无
* @Date: 2023-04-22 00:33:28
*/
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
// 触发空闲中断
if(SET == __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
{
// 清除空闲中断标志位
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
// 停止DMA接收
HAL_UART_DMAStop(&huart1);
// 计算长度
MyUSART1_Data.Usart1_Rx_Len = USART1_MAX_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
// 接收标志位置1
MyUSART1_Data.Usart1_Rx_Over_Flag = 1;
}
}
/*
* @description: 重定向printf
* @param {int} ch
* @param {FILE} *f
* @return: int
* @Date: 2023-04-22 03:01:47
*/
int fputc(int ch, FILE *f)
{
uint32_t temp = ch;
HAL_UART_Transmit(&huart1, (uint8_t *)&temp, 1, 1000);
return ch;
}
MyUSART3.h
#ifndef __MYUSART3_H
#define __MYUSART3_H
#include "MyAll.h"
// USART3接收的最大长度数据
#define USART3_MAX_LEN 50
typedef struct
{
uint16_t Usart3_Rx_Sta;
// USART3接收数组
uint8_t Usart3_Rx_Buff[USART3_MAX_LEN];
// USART3接收长度
uint8_t Usart3_Rx_Len;
// USART3接收完成标志位
bool Usart3_Rx_Over_Flag;
// 接收的最新数据
uint8_t Usart3_New_Data;
} MyUSART3_TypeDef;
extern MyUSART3_TypeDef MyUSART3_Data;
#endif
MyUSART3.c
/*
*@Description: 串口3
*@Author: Yang
*@Date: 2023-04-22 13:17:59
*/
#include "MyUSART3.h"
/*====================================变量区 BEGIN====================================*/
MyUSART3_TypeDef MyUSART3_Data =
{
.Usart3_Rx_Sta = 0,
.Usart3_Rx_Buff = {0},
.Usart3_Rx_Len = 0,
.Usart3_Rx_Over_Flag = 0,
.Usart3_New_Data = 0
};
/*====================================变量区 END====================================*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(&huart3 == huart)
{
// 接收完的一批数据,还没有被处理,则不再接收其他数据
if(0 == (MyUSART3_Data.Usart3_Rx_Sta & (1 << 15)))
{
if(MyUSART3_Data.Usart3_Rx_Sta < USART3_MAX_LEN) //还可以接收数据
{
TIM3->CNT = 0;//计数器清空
if(0 == MyUSART3_Data.Usart3_Rx_Sta) //使能定时器7的中断
{
HAL_TIM_Base_Start_IT(&htim3);
}
MyUSART3_Data.Usart3_Rx_Buff[MyUSART3_Data.Usart3_Rx_Sta++] = MyUSART3_Data.Usart3_New_Data; //记录接收到的值
}
else
{
MyUSART3_Data.Usart3_New_Data |= 1 << 15; //强制标记接收完成
}
}
HAL_UART_Receive_IT(&huart3, (uint8_t *)&MyUSART3_Data.Usart3_New_Data, 1); // 串口3接收中断
}
}
MyUSART2.h
/*
*@Description: 串口2
*@Author: Yang
*@Date: 2023-04-29 07:33:42
*/
#ifndef __MYUSART2_H
#define __MYUSART2_H
#include "MyAll.h"
// USART2接收的最大长度数据
#define USART2_MAX_LEN 100
typedef struct
{
// 接收app发送过来的有效命令标志位
bool APP_Rx_Flag;
// USART2接收数组
uint8_t Usart2_Rx_Buff[USART2_MAX_LEN];
// USART2接收长度
uint8_t Usart2_Rx_Len;
// USART2接收完成标志位
bool Usart2_Rx_Over_Flag;
void (*vUsart2_Rx_Data_Analytic)(void);
void (*WIFI_printf)(char *, ...);
void (*WIFI_TCP_Send)(char *, ...);
} MyUSART2_TypeDef;
extern MyUSART2_TypeDef MyUSART2_Data;
extern uint16_t Client_Id;
void vUsart2_Rx_Data_Analytic(void);
void WIFI_printf (char *fmt, ...);
void WIFI_TCP_Send(char *fmt, ...);
#endif
MyUSART2.c
/*
*@Description: 串口2
*@Author: Yang
*@Date: 2023-04-29 07:33:32
*/
#include "MyUSART2.h"
/*====================================变量区 BEGIN====================================*/
MyUSART2_TypeDef MyUSART2_Data =
{
.APP_Rx_Flag = 0,
.Usart2_Rx_Buff = {0},
.Usart2_Rx_Len = 0,
.Usart2_Rx_Over_Flag = 0,
.vUsart2_Rx_Data_Analytic = &vUsart2_Rx_Data_Analytic,
.WIFI_printf = &WIFI_printf,
.WIFI_TCP_Send = &WIFI_TCP_Send
};
extern DMA_HandleTypeDef hdma_usart2_rx;
/*====================================变量区 END====================================*/
uint16_t Client_Id = 0;
/*
* @description: USART2数据解析 【接收格式:0x0D0x0A+IPD,<link ID>,<len>:后面是数据内容】数据格式:0x55 0xXX 0xXX 0xXX 0xBB
* @return: 无
* @Date: 2023-04-22 00:45:56
*/
/*
帧头--0x55 帧尾--0xBB
-----------------------------------------------------------
| 主指令 | 副指令[1] | 副指令[2] | 说明 |
|-----------|--------------|--------------|-----------------|
| 0x01 | 0x01 | 0x00 |打开舵机
|------------|-------------|--------------|-----------------|
| | | |
|------------|-------------|--------------|-----------------|
*/
// USART2数据解析(只在待机和主页面才执行这个函数)
void vUsart2_Rx_Data_Analytic(void)
{
if (MyUSART2_Data.Usart2_Rx_Over_Flag)
{
MyUSART2_Data.Usart2_Rx_Over_Flag = 0;
if((0x0D == MyUSART2_Data.Usart2_Rx_Buff[0]) &&
(0x0A == MyUSART2_Data.Usart2_Rx_Buff[1]) &&
('+' == MyUSART2_Data.Usart2_Rx_Buff[2]) &&
('I' == MyUSART2_Data.Usart2_Rx_Buff[3]) &&
('P' == MyUSART2_Data.Usart2_Rx_Buff[4]) &&
('D' == MyUSART2_Data.Usart2_Rx_Buff[5]))
{
if((0x55 == MyUSART2_Data.Usart2_Rx_Buff[11]) && (0xBB == MyUSART2_Data.Usart2_Rx_Buff[15])) // 判断帧头
{
Client_Id = MyUSART2_Data.Usart2_Rx_Buff[7] - '0';
switch(MyUSART2_Data.Usart2_Rx_Buff[12]) // 判断主指令
{
case 0x01:
{
// 控制舵机开
if((0x01 == MyUSART2_Data.Usart2_Rx_Buff[13]) && 0x00 == MyUSART2_Data.Usart2_Rx_Buff[14]) // 判断副1,2
{
SG90_Data.vSG90_Mode_switch();
Menu_Data.Menu_State = INTERFACE_1;
Menu_Data.Menu_Flag_Buff[0] = 0;
WIFI_TCP_Send("OK\r\n");
}
break;
}
default:
break;
}
MyUSART2_Data.APP_Rx_Flag = 1;
}
}
MyAll_Data.vTime_Out_Init(); //超时等待清0
memset(MyUSART2_Data.Usart2_Rx_Buff, 0, sizeof(MyUSART2_Data.Usart2_Rx_Buff));
}
}
void USART2_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart2);
// 触发空闲中断
if (SET == __HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE))
{
// 清除空闲中断标志位
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
// 停止DMA接收
HAL_UART_DMAStop(&huart2);
// 计算长度
MyUSART2_Data.Usart2_Rx_Len = USART2_MAX_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
// 接收标志位置1
MyUSART2_Data.Usart2_Rx_Over_Flag = 1;
}
// 重新打开DMA接收
HAL_UART_Receive_DMA(&huart2, (uint8_t *)MyUSART2_Data.Usart2_Rx_Buff, USART2_MAX_LEN);
}
/*
* @description: 发送到串口2函数
* @param {char} *fmt
* @return {*}
* @Date: 2023-04-29 09:02:55
*/
// 发送到串口2函数
void WIFI_printf (char *fmt, ...)
{
char buff[USART2_MAX_LEN + 1]; //用于存放转换后的数据 [长度]
uint16_t i = 0;
__va_list arg_ptr;
va_start(arg_ptr, fmt);
vsnprintf(buff, USART2_MAX_LEN + 1, fmt, arg_ptr); //数据转换
i = strlen(buff); //得出数据长度
if(strlen(buff) > USART2_MAX_LEN)
{
i = USART2_MAX_LEN; //如果长度大于最大值,则长度等于最大值(多出部分忽略)
}
HAL_UART_Transmit(&huart2, (uint8_t *)buff, i, 0xffff); //串口发送函数(串口号,内容,数量,溢出时间)
va_end(arg_ptr);
}
/*
* @description: TCP模式下的数据发送
* @param {char} *fmt
* @return {*}
* @Date: 2023-05-03 15:06:14
*/
// TCP模式下的数据发送
void WIFI_TCP_Send(char *fmt, ...)
{
char buff[USART2_MAX_LEN + 1]; //用于存放转换后的数据 [长度]
uint16_t i = 0;
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsnprintf(buff, USART2_MAX_LEN + 1, fmt, arg_ptr); //数据转换
i = strlen(buff); //得出数据长度
if(strlen(buff) > USART2_MAX_LEN)
{
i = USART2_MAX_LEN; //如果长度大于最大值,则长度等于最大值(多出部分忽略)
}
MyUSART2_Data.WIFI_printf("AT+CIPSEND=%d,%d\r\n", Client_Id, i); //先发送AT指令和数据数量
HAL_Delay(150);//毫秒延时等待WIFI模块返回">",此处没做返回是不是">"的判断。稳定性要求高的项目要另加判断。
HAL_UART_Transmit(&huart2, (uint8_t *)buff, i, 0xffff); //发送数据内容(串口号,内容,数量,溢出时间)
va_end(arg_ptr);
}
看门狗
定时6s,然后5.8s在滴答定时器中断里进行喂狗一次
WiFi
- 接线(看模块丝印,正负极不要接反!),这里只需要接5个脚就够了
编号 | 名称 | STM32管脚 |
---|---|---|
1 | GND | GND |
4 | RX | USART2_TX |
5 | TX | USART2_RX |
6 | EN | 3.3V |
8 | VCC | 3.3V |
2 | GPIO2 | 看情况 |
3 | GPIO0 | 烧固件需要接地 |
7 | RST | 悬空,烧固件需要快速接地然后悬空,否则一直显示等待上电 |
- 三种模式
STA(MODE1)
:类似一个接收模式,就是开启该模式后,你只能通过串口给他发送信息,ESP8266只负责接收,而不会产生一个热点(手机或其他设备是找不到热点的)
这种模式更加适用于需要联网,ESP8266作为一种物联网设备,需要通过Wi-Fi接入互联网,以便与其他设备进行通信、远程监测和控制等操作,可以使用ESP8266的STA模式通过互联网连接到远程服务器,以便实现设备的远程监控和控制
//tcp服务器 AT+CWMODE=1 设置成sta模式 AT+RST 重启生效 AT+CWMODE? 查询WiFi模块的模式 AT+CWJAP="111","12345678" 连接wifi名字以及密码 AT+CIPMUX=1 设置多连接 AT+CIPSERVER=1,8899 设置端口号 AT+CIFSR 查询路由器分配的ip地址 // 服务器发送给客户端数据格式是 AT+CIPSEND=ID,LEN // ID是客户端的ID因为连接到ESP8266有很多个它不能全部都发送只能发送给指定ID,LEN是发送的数据长度
//tcp客户端 AT+CWMODE=2 设置成sta模式 AT+RST 重启生效 AT+CWMODE? 查询WiFi模块的模式 AT+CWJAP="111","12345678" 连接wifi名字以及密码 AT+CIPMUX=0 设置单连接 AT+CIPSTART="TCP","10.128.19.xxx",1121 这个需要根据手机端打开的tcp服务器的ip地址和端口号来修改 AT+CIPMODE=1 开启透传模式(仅单连接 客服端时支持) AT+CIPSEND 开始传送数据
AP(MODE2)
:发出去一个热点,供手机或其他设备连接,如果要实现手机连热点来控制单片机,那么该模式就要开启了
下面两种主要区别是esp8266当做客户端时可以自动发送采集的数据给手机端服务器端,适用于一直采集接收的场景;esp8266当做服务器端时不会主动发送采集的数据给手机客户端,需要手机客户端发送请求命令才能,适用于比如控制模块、设备状态查询等功能
// tcp服务器 AT+CWMODE=2 设置成ap模式 AT+RST 重启生效 AT+CWMODE? 查询WiFi模块的模式 AT+CWSAP="ESP8266","12345678",11,0 设置要产生的wifi名字以及密码 AT+CIPMUX=1 设置多接入点模式 AT+CIPSERVER=1,8899 设置端口号 AT+CIFSR 查询路由器分配的ip地址
设置完上面后,手机下载WiFi调试助手,选择
TCP客户端
,写上WiFI模块的IP地址还有端口号即可连接,然后可以进行发送数据,在程序里串口接收需要判断发送过来的数据,格式为:0x0D0x0A+IPD,<link ID>,<len>:后面是数据内容
,其中0x0D0x0A是换行, 表示连接标识符,标识着网络连接的唯一性,一个 ESP8266 模块可以同时与多个客户端建立 TCP 或 UDP 连接,每个连接都有一个唯一的标识符,CP 和 UDP 连接的 link ID 编号从 0 开始递增,根据连接建立的顺序,越早建立的连接 link ID 越小;表示数据的长度大小 定好通讯协议,我的通讯协议是:
0x55 0xXX 0xXX 0xXX 0xBB,其中0x55表示帧头,第一个0xXX是主指令,第二个0xXX是副1指令,第三个0xXX是副2指令,0xBB是帧尾,这里我没加校验和因为手机发送的话需要计算校验和比较麻烦
// tcp客户端 AT+CWMODE=2 设置成ap模式 AT+RST 重启生效 AT+CWMODE? 查询WiFi模块的模式 AT+CWSAP="ESP8266","12345678",11,0 设置要产生的wifi名字以及密码 AT+CIPMUX=0 设置单连接 AT+CIPSTART="TCP","10.128.19.xxx",1121 这个需要根据手机端打开的tcp服务器的ip地址和端口号来修改 AT+CIPMODE=1 开启透传模式(仅单连接 客服端时支持) AT+CIPSEND 开始传送数据
STA+AP(MODE3)
:以上两种同时实现
- 传输方式一般选择透传
如果不采用透传模式,那么每发送一次数据都要发送一次 AT+CIPSEND=<param>
的指令就显得尤为麻烦,因此模式一般设置为透传模式,退出透传模式就给指令 +++
即可
- 一般下ESP8266会生成一个固定的IP地址,根据不同模块地址也不一样(我的是192.168.4.1),然而如果有其他设备比如手机连接到这个ESP8266则该手机会被分配一个固定IP地址(192.168.4.x),用于跟ESP8266进行通信的
程序编写
ESP8266.h
/*
*@Description:
*@Author: Yang
*@Date: 2023-04-29 07:35:05
*/
#ifndef __ESP8266_H
#define __ESP8266_H
#include "MyAll.h"
// ESP8266返回值--成功
#define ESP8266_PASS 1
// ESP8266返回值--失败
#define ESP8266_FAIL 0
// 响应值--OK
#define ESP8266_RETURN_OK "OK"
// 发一条AT普通指令所等待时间,需要*10才是最终所等待的时间ms
#define ESP8266_WAIT_TIME 100
// 宏参数转字符串常量
#define TO_STRING(x) #x
/***********用户数据*************/
// 热点/路由器名称(看实际改)
#define SSID "yang520"
// 热点/路由器密码(看实际改)
#define SSID_PASSWORD "00000000"
// 云服务器IP地址【必须按您的实际情况修改】
# define TCP_IP "iot-06z00b2xuy7fxl9.mqtt.iothub.aliyuncs.com"
// 云服务器端口号
# define TCP_PORT 1883
/********常用指令(以下指令都是掉电不保存)********/
// 查看模块是否在线/正常
#define ESP8266_CMD_ONLINE "AT"
// 设置工作模式为STA
#define ESP8266_CMD_MODE_STA "AT+CWMODE=1"
// 设置工作模式为AP
#define ESP8266_CMD_MODE_AP "AT+CWMODE=2"
// 设置工作模式为STA+AP
#define ESP8266_CMD_MODE_STAAP "AT+CWMODE=3"
// 恢复出厂设置(擦除所有Flash参数恢复为默认值,会导致模块重启)
#define ESP8266_CMD_RESTORE "AT+RESTORE"
// 模块重启
#define ESP8266_CMD_REBOOT "AT+RST"
// 设置ESP8266的名称,密码,通道号,加密方式(AP模式下此命令才有效)
#define ESP8266_CMD_SETTING "AT+CWSAP=\"ESP8266_401\",\"66666666\",1,4"
// 上电是否自动连接AP(0--不自动 1--自动)
#define ESP8266_CMD_CONNECT_AP(num) "AT+CWAUTOCONN="TO_STRING(num)
// 是否开启回显(0--关闭 1--开启)
#define ESP8266_CMD_SWCH_ECHO(num) "ATE"TO_STRING(num)
// 设置DHCP(参1:模式1/2/3 参2:0-关闭 1-开启)
#define ESP8266_CMD_DHCP "AT+CWDHCP=1,1"
// 进入单路/多路连接模式(0--单 1--多)
#define ESP8266_CMD_ENTER_MUX(num) "AT+CIPMUX="TO_STRING(num)
// 进入透传模式
#define ESP8266_CMD_MUX_MODE "AT+CIPMODE=1"
// 开始传输数据(如果需要发命令需退出透传)
#define ESP8266_CMD_MUX_START "AT+CIPSEND"
// 设置端口号(参1:1-建立服务器 0-关闭服务器 参2:端口号默认333范围是1~65535)
#define ESP8266_CMD_SET_PORT "AT+CIPSERVER=1,8899"
// 查询路由分配的IP地址
#define ESP8266_CMD_FIND_IP "AT+CIFSR"
typedef struct
{
// 当前ESP8266属于哪种状态(1--设为服务器端 2--设为客户端 0--无)
uint8_t ESP8266_State;
// 透传标志位(0--非透传 1--透传)
bool MUX_Flag;
// ESP连接服务器时IP的最后一位
uint16_t Server_IP_Number;
// ESP连接服务器时的端口
uint16_t Server_Port_Number;
// AP模式下端口号
uint16_t ESP8266_AP_Port;
uint8_t (*ucESP8266_Send_Cmd)(uint8_t *, uint8_t *, uint16_t);
uint8_t (*ucESP8266_Check_Cmd)(uint8_t *);
char *(*cpcESP8266_Success_Message)(uint8_t);
uint8_t (*ucESP8266_Quit_Mux)(void);
uint8_t (*ucESP8266_Connect_AP)(void);
uint8_t (*ucESP8266_Connect_Server)(char *, uint16_t);
uint8_t (*ucESP8266_Set_Server)(void);
char *(*pcESP8266_Check_IP)(void);
uint8_t (*ucESP8266_Set_Client)(void);
} ESP8266_TypeDef;
extern ESP8266_TypeDef ESP8266_Data;
uint8_t ucESP8266_Send_Cmd(uint8_t *cmd, uint8_t *ack, uint16_t wait_time);
uint8_t ucESP8266_Check_Cmd(uint8_t *str);
const char *cpcESP8266_Success_Message(uint8_t ensure);
uint8_t ucESP8266_Quit_Mux(void);
uint8_t ucESP8266_Connect_AP(void);
uint8_t ucESP8266_Connect_Server(char *str, uint16_t _port);
uint8_t ucESP8266_Set_Server(void);
char *pcESP8266_Check_IP(void);
uint8_t ucESP8266_Set_Client(void);
#endif
ESP8266.c
#include "ESP8266.h"
/*====================================变量区 BEGIN====================================*/
ESP8266_TypeDef ESP8266_Data =
{
.ESP8266_State = 0,
.MUX_Flag = 0,
.Server_IP_Number = 0,
.Server_Port_Number = 8000,
.ESP8266_AP_Port = 0,
.ucESP8266_Send_Cmd = &ucESP8266_Send_Cmd,
.ucESP8266_Check_Cmd = &ucESP8266_Check_Cmd,
.cpcESP8266_Success_Message = &cpcESP8266_Success_Message,
.ucESP8266_Quit_Mux = &ucESP8266_Quit_Mux,
.ucESP8266_Connect_AP = &ucESP8266_Connect_AP,
.ucESP8266_Connect_Server = &ucESP8266_Connect_Server,
.ucESP8266_Set_Server = &ucESP8266_Set_Server,
.pcESP8266_Check_IP = &pcESP8266_Check_IP,
.ucESP8266_Set_Client = &ucESP8266_Set_Client
};
/*====================================变量区 END====================================*/
/*
* @description: 设置为客户端
* @return {*} 设置成功返回0 其余返回表示失败
* @Date: 2023-05-03 16:16:24
*/
// 设置为客户端
uint8_t ucESP8266_Set_Client(void)
{
uint16_t ip = 0;
uint16_t port = 8000;
uint8_t arr[40] = {0};
Oled_Data.vOled_Clear();
Oled_Data.vOled_Display_Gb2312_String(0, 0, (uint8_t *)"正在设置...");
ESP8266_Data.ucESP8266_Quit_Mux();
if(ESP8266_FAIL == ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_MODE_AP, ESP8266_RETURN_OK, ESP8266_WAIT_TIME))
{
return 3;
}
if(ESP8266_FAIL == ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_REBOOT, ESP8266_RETURN_OK, ESP8266_WAIT_TIME))
{
return 6;
}
HAL_Delay(5000);
if(ESP8266_FAIL == ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_ONLINE, ESP8266_RETURN_OK, ESP8266_WAIT_TIME))
{
return 1;
}
ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_SETTING, ESP8266_RETURN_OK, ESP8266_WAIT_TIME);
if(ESP8266_FAIL == ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_ENTER_MUX(0), ESP8266_RETURN_OK, ESP8266_WAIT_TIME))
{
return 11;
}
Oled_Data.vOled_Clear();
Oled_Data.vOled_Display_Gb2312_String(0, 0, (uint8_t *)"TCP服务端IP");
Oled_Data.vOled_Display_Gb2312_String(0, 6, (uint8_t *)"返回");
Oled_Data.vOled_Display_Gb2312_String(47, 6, (uint8_t *)"增/减");
Oled_Data.vOled_Display_Gb2312_String(96, 6, (uint8_t *)"确认");
while(1)
{
if (Key_Data.Key_Down_Buff[0])
{
Key_Data.Key_Down_Buff[0] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
return 16;
}
else if(Key_Data.Key_Down_Buff[1])
{
Key_Data.Key_Down_Buff[1] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
ip++;
if(ip >= 250)
{
ip = 250;
}
}
else if(Key_Data.Key_Down_Buff[4])
{
Key_Data.Key_Down_Buff[4] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
while(ip != 0)
{
ip--;
break;
}
}
else if(Key_Data.Key_Down_Buff[2])
{
Key_Data.Key_Down_Buff[2] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
break;
}
snprintf(arr, sizeof(arr), "IP: 192.168.4.%d", ip);
Oled_Data.vOled_Display_String_5x7(0, 3, (uint8_t *)arr);
}
Oled_Data.vOled_Clear();
Oled_Data.vOled_Display_Gb2312_String(0, 0, (uint8_t *)"TCP端口输入");
Oled_Data.vOled_Display_Gb2312_String(0, 6, (uint8_t *)"返回");
Oled_Data.vOled_Display_Gb2312_String(47, 6, (uint8_t *)"增/减");
Oled_Data.vOled_Display_Gb2312_String(96, 6, (uint8_t *)"确认");
while(1)
{
if (Key_Data.Key_Down_Buff[0])
{
Key_Data.Key_Down_Buff[0] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
return 16;
}
else if(Key_Data.Key_Down_Buff[1])
{
Key_Data.Key_Down_Buff[1] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
port++;
if(port >= 9000)
{
port = 9000;
}
}
else if(Key_Data.Key_Down_Buff[4])
{
Key_Data.Key_Down_Buff[4] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
while(port != 8000)
{
port--;
break;
}
}
else if(Key_Data.Key_Down_Buff[2])
{
Key_Data.Key_Down_Buff[2] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
break;
}
snprintf(arr, sizeof(arr), "端口: %d", port);
Oled_Data.vOled_Display_Gb2312_String(0, 3, (uint8_t *)arr);
}
ESP8266_Data.Server_IP_Number = ip;
ESP8266_Data.Server_Port_Number = port;
memset(arr, 0, sizeof(arr));
snprintf(arr, sizeof(arr), "192.168.4.%d", ESP8266_Data.Server_IP_Number);
ESP8266_Data.ucESP8266_Connect_Server(arr, ESP8266_Data.Server_Port_Number);
ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_MUX_MODE, ESP8266_RETURN_OK, ESP8266_WAIT_TIME);
ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_MUX_START, ESP8266_RETURN_OK, ESP8266_WAIT_TIME);
ESP8266_Data.MUX_Flag = 1;
return 0;
}
/*
* @description: 查找ESP8266模块的IP
* @return {*} 返回IP地址
* @Date: 2023-05-03 13:19:06
*/
// 查找ESP8266模块的IP
char *pcESP8266_Check_IP(void)
{
ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_FIND_IP, "APIP", 1000);
char *ptr = strstr(MyUSART2_Data.Usart2_Rx_Buff, "APIP"); // 查找 "APIP" 子字符串的位置
if (ptr != NULL)
{
char *qtr = strchr(ptr, '\"'); // 查找第一个双引号位置
if (qtr != NULL)
{
char *rtr = strchr(qtr + 1, '\"'); // 查找第二个双引号位置
if (rtr != NULL)
{
char *ip = (char *) malloc(rtr - qtr); // 使用malloc()函数分配空间
memset(ip, 0, rtr - qtr); // 初始化缓冲区(不要用sizeof不然末尾有一个乱码字符R)
strncpy(ip, qtr + 1, rtr - qtr - 1); // 拷贝 IP 地址到缓冲区
return ip;
}
}
}
return NULL;
}
/*
* @description: 设为服务器端
* @return {*} 返回0表示设置成功 其余失败
* @Date: 2023-05-02 16:34:54
*/
// 设为服务器端
uint8_t ucESP8266_Set_Server(void)
{
uint8_t arr[30];
uint16_t Port = 8000; // 端口
char *p = (char *)malloc(50); // 分配存储空间的指针
Oled_Data.vOled_Clear();
Oled_Data.vOled_Display_Gb2312_String(0, 0, (uint8_t *)"正在设置...");
ESP8266_Data.ucESP8266_Quit_Mux();
if(ESP8266_FAIL == ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_MODE_AP, ESP8266_RETURN_OK, ESP8266_WAIT_TIME))
{
return 3;
}
if(ESP8266_FAIL == ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_REBOOT, ESP8266_RETURN_OK, ESP8266_WAIT_TIME))
{
return 6;
}
HAL_Delay(5000);
if(ESP8266_FAIL == ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_ONLINE, ESP8266_RETURN_OK, ESP8266_WAIT_TIME))
{
return 1;
}
ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_SETTING, ESP8266_RETURN_OK, ESP8266_WAIT_TIME);
if(ESP8266_FAIL == ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_ENTER_MUX(1), ESP8266_RETURN_OK, ESP8266_WAIT_TIME))
{
return 11;
}
Oled_Data.vOled_Display_Gb2312_String(0, 6, (uint8_t *)"返回");
Oled_Data.vOled_Display_Gb2312_String(47, 6, (uint8_t *)"增/减");
Oled_Data.vOled_Display_Gb2312_String(96, 6, (uint8_t *)"确认");
while(1)
{
if (Key_Data.Key_Down_Buff[0])
{
Key_Data.Key_Down_Buff[0] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
return 14;
}
else if(Key_Data.Key_Down_Buff[1])
{
Key_Data.Key_Down_Buff[1] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
Port += 2;
if(Port >= 9000)
{
Port = 9000;
}
}
else if(Key_Data.Key_Down_Buff[4])
{
Key_Data.Key_Down_Buff[4] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
Port -= 2;
if(Port <= 8000)
{
Port = 8000;
}
}
else if(Key_Data.Key_Down_Buff[2])
{
Key_Data.Key_Down_Buff[2] = 0;
Buzzer_Data.vBuzzer_Ring(); // 蜂鸣器滴一下
ESP8266_Data.ESP8266_AP_Port = Port;
sprintf((char *)p, "AT+CIPSERVER=1,%d", ESP8266_Data.ESP8266_AP_Port); // 发送连接AT指令
ESP8266_Data.ucESP8266_Send_Cmd(p, ESP8266_RETURN_OK, ESP8266_WAIT_TIME);
free(p); // 释放分配的空间和指针
Oled_Data.vOled_Clear();
Oled_Data.vOled_Display_Gb2312_String(0, 0, (uint8_t *)"正在设置...");
break;
}
snprintf(arr, sizeof(arr), "选择端口:%d", Port);
Oled_Data.vOled_Display_Gb2312_String(0, 2, (uint8_t *)arr);
}
return 0;
}
/*
* @description: ESP8266连接到服务器
* @return {*}
* @Date: 2023-04-29 15:11:21
*/
// ESP8266连接到服务器
uint8_t ucESP8266_Connect_Server(char *str, uint16_t _port)
{
uint8_t i = 10;
char *p = (char *)malloc(50); //分配存储空间的指针
sprintf((char *)p, "AT+CIPSTART=\"TCP\",\"%s\",%d", str, _port);
while(i)
{
if(ESP8266_PASS == ESP8266_Data.ucESP8266_Send_Cmd(p, "CONNECT", 1000))
{
break;
}
i--;
}
free(p); // 释放分配的空间和指针
if(i)
{
return ESP8266_PASS;
}
else
{
return ESP8266_FAIL;
}
}
/*
* @description: ESP8266连接AP设备(无线路由器)
* @return {*}
* @Date: 2023-04-29 14:49:55
*/
//ESP8266连接AP设备(无线路由器)
uint8_t ucESP8266_Connect_AP(void)
{
uint8_t i = 10;
char *p = (char *)malloc(50); // 分配存储空间的指针
sprintf((char *)p, "AT+CWJAP=\"%s\",\"%s\"", SSID, SSID_PASSWORD); // 发送连接AT指令
while(i) // 循环判断等待连接AP的结果
{
if(ESP8266_PASS == ESP8266_Data.ucESP8266_Send_Cmd(p, "WIFI GOT IP", 1000))
{
break;
}
i--;
}
free(p); // 释放分配的空间和指针
if(i)
{
return ESP8266_PASS;
}
else
{
return ESP8266_FAIL;
}
}
/*
* @description: 退出透传模式
* @return {*}
* @Date: 2023-04-29 14:31:09
*/
// 退出透传模式
uint8_t ucESP8266_Quit_Mux(void)
{
uint8_t result;
MyUSART2_Data.WIFI_printf("+++");
HAL_Delay(1000);
MyUSART2_Data.WIFI_printf("+++");
HAL_Delay(1000);
ESP8266_Data.MUX_Flag = 0;
result = ESP8266_Data.ucESP8266_Send_Cmd(ESP8266_CMD_ONLINE, ESP8266_RETURN_OK, ESP8266_WAIT_TIME);
return result;
}
/*
* @description: 向ESP8266发送指令
* @param {uint8_t} *cmd 指令字串符
* @param {uint8_t} *ack 响应字串符
* @param {uint16_t} wait_time 超时等待
* @return {*} ESP8266_FAIL--失败 ESP8266_PASS--成功
* @Date: 2023-04-29 11:42:56
*/
// 向ESP8266发送指令
uint8_t ucESP8266_Send_Cmd(uint8_t *cmd, uint8_t *ack, uint16_t wait_time)
{
uint8_t res = ESP8266_PASS;
MyUSART2_Data.Usart2_Rx_Over_Flag = 0; // 接收标志位清0
memset(MyUSART2_Data.Usart2_Rx_Buff, 0, sizeof(MyUSART2_Data.Usart2_Rx_Buff)); // 接收数组清0
MyUSART2_Data.WIFI_printf("%s\r\n", cmd); // 发送指令
if (ack && wait_time) // 响应值不为NULL且超时时间不为0则进入if
{
while (--wait_time)
{
HAL_Delay(10); // 延时
if (MyUSART2_Data.Usart2_Rx_Over_Flag) // 判断接收标志位是否置1
{
if (ESP8266_Data.ucESP8266_Check_Cmd(ack)) // 检查ESP8266回传的响应值是否跟ack一致
{
res = ESP8266_PASS;
}
else
{
res = ESP8266_FAIL;
}
MyUSART2_Data.Usart2_Rx_Over_Flag = 0;
return res;
}
}
if (0 == wait_time)
{
res = ESP8266_FAIL;
}
}
return res;
}
/*
* @description: ESP8266检查指令
* @param {uint8_t} *str 需要查找的字符串
* @return {*} ESP8266_FAIL--失败 ESP8266_PASS--成功
* @Date: 2023-04-29 11:55:25
*/
// ESP8266检查指令
uint8_t ucESP8266_Check_Cmd(uint8_t *str)
{
uint8_t res = ESP8266_FAIL;
if (MyUSART2_Data.Usart2_Rx_Over_Flag)
{
if (strstr((const char *)MyUSART2_Data.Usart2_Rx_Buff, (const char *)str)) // 查找str在串口接收数组中第一次出现的位置,找到返回对应位置指针,找不到返回NULL
{
res = ESP8266_PASS;
}
}
return res;
}
/*
* @description: 各种失败信息
* @param {uint8_t} ensure 错误信息编号
* @return {*} 失败信息字符串
* @Date: 2023-04-29 13:53:04
*/
// 各种失败信息
const char *cpcESP8266_Success_Message(uint8_t ensure)
{
const char *p;
Oled_Data.vOled_Clear();
switch(ensure)
{
case 1:
{
p = "模块不在线";
break;
}
case 2:
{
p = "设置为STA失败";
break;
}
case 3:
{
p = "设置为AP失败";
break;
}
case 4:
{
p = "设置为STP失败";
break;
}
case 5:
{
p = "恢复出厂设置失败";
break;
}
case 6:
{
p = "模块重启失败";
break;
}
case 7:
{
p = "设置模块参数失败";
break;
}
case 8:
{
p = "设置自动连接失败";
break;
}
case 9:
{
p = "设置回显失败";
break;
}
case 10:
{
p = "设置DHCP失败";
break;
}
case 11:
{
p = "进入单多连接失败";
break;
}
case 12:
{
p = "进入透传模式失败";
break;
}
case 13:
{
p = "开始传输数据失败";
break;
}
case 14:
{
p = "设置端口号失败";
break;
}
case 15:
{
p = "查询IP地址失败";
break;
}
case 16:
{
p = "连接服务器失败";
break;
}
case 17:
{
p = "退出透传失败";
break;
}
case 18:
{
p = "连接路由失败";
break;
}
case 19:
{
p = "开启SNTP失败";
break;
}
case 20:
{
p = "配置信息失败";
break;
}
case 21:
{
p = "连接URL失败";
break;
}
case 22:
{
p = "订阅失败";
break;
}
case 23:
{
p = "发布失败";
break;
}
default:
break;
}
return p;
}
固件烧录
以下步骤是在 ESP8266-01S
型号下进行的
WiFi烧固件除了上面所说的引脚要接外还有 GPIO0
引脚需要接地(接地为下载状态;悬空为工作状态); RST引脚需要悬空
,当提示等待上电同步时接地并迅速悬空,复位模块
- 烧录MQTT固件步骤
- 使用USB转串口按上面要求接线好
- 前往安信可官网找到
固件号:1471
下载- 打开烧录软件
flash_download_tool_v3.8.5.exe
,点击ESP8266 DownloadTool
- 这是一般参数
- 下载完成测试
将ESP8266-01s的引脚IO0拉高(不接GND/置空)
ESP8266-01s模块重新上电,打开串口助手
在串口助手发送AT+GMR指令,输出版本信息则说明下载成功
串口助手连接MQTT
下面是使用串口来进行MQTT的连接测试
- 创建产品那些就不写了普通步骤,创建完后点击添加自定义功能设置一下参数,添加一个功能属性,设置完成之后点击发布上线。
- 开始在串口助手那输入命令进行连接(前提是ESP8266已经烧了MQTT固件)
- 复位
AT+RST
- 恢复出厂设置(这个不会把你烧的固件也恢复到出厂的放心…)
AT+RESTORE
- 配置为STA模式
AT+CWMODE=1
- 开启SNTP服务器,8时域,SNTP服务器为阿里云域名(这样ESP8266 就可以通过 ntp1.aliyun.com 获取准确的网络时间,在进行短信发送、数据上传等操作时能够更加精确地记录时间戳)
CIPSNTPCFG:设置 SNTP(简单网络时间协议)配置参数
1:SNTP 服务器数量,这里设置为 1
8:时区偏移量,这里设置为 GMT+8(东八区)
"ntp1.aliyun.com":SNTP 服务器地址,这里设置为阿里云提供的ntp1.aliyun.com,如果使用别的物联网平台需要看它的这个地址是哪个
AT+CIPSNTPCFG=1,8,"ntp1.aliyun.com"
- 连接手机热点或者其他路由
AT+CWJAP="yang520","00000000"
- 配置 MQTT 用户属性
username和password可以在
设备
–设备信息
–MQTT连接参数
那查看
MQTTUSERCFG
:设置 MQTT 用户名和密码
0
:MQTT 客户端编号
1
: MQTT 服务器的 SECUREMODE,这里设置为 1,即使用 SSL 安全连接
"NULL"
: MQTT 服务器的 CA 证书地址,这里设置为 NULL,表示不需要 CA 证书验证
"username"
:MQTT 服务器的用户名
"passwd":MQTT 服务器的密码
0:MQTT 连接的会话类型,这里设置为 0,即非持久化会话
0:MQTT 连接的 KEEPALIVE 参数,这里设置为 0,表示不启用 KEEPALIVE
"":MQTT 连接的遗嘱主题,这里设置为 "",表示不需要遗嘱功能
AT+MQTTUSERCFG=0,1,"NULL","username","passwd",0,0,""
# 示例
AT+MQTTUSERCFG=0,1,"NULL","ESP8266&ikjy4ej7BOp","67078699f9d2d3d42be9fe3e141b1cd752c46d226cf4d5c88275c1a260815136",0,0,""
- 配置 MQTT 客户端 ID
clientId可以在
设备
–设备信息
–MQTT连接参数
那查看
注意
:第二个参数中有逗号的需在逗号前添加 \
MQTTCLIENTID:设置 MQTT 客户端 ID
0:MQTT 客户端编号
"clientId":MQTT 客户
AT+MQTTCLIENTID=0,"clientId"
# 示例
AT+MQTTCLIENTID=0,"ikjy4ej7BOp.ESP8266|securemode=2\,signmethod=hmacsha256\,timestamp=1682842914176|"
- 连接/查询 MQTT Broker
mqttHostUrl和port可以在
设备
–设备信息
–MQTT连接参数
那查看
MQTTCONN:建立 MQTT 连接
0:MQTT 客户端编号
"mqttHostUrl":MQTT 服务器的 URL 地址
port:MQTT 服务器的端口号,通常为 1883
1:MQTT 连接的 CLEAN SESSION 参数,这里设置为 1,即每次连接时清除会话信息
AT+MQTTCONN=0,"mqttHostUrl",port,1
# 示例
AT+MQTTCONN=0,"iot-06z00b2xuy7fxl9.mqtt.iothub.aliyuncs.com",1883,1
- 订阅指令
${deviceName}
需要替换你的设备名称
MQTTSUB:订阅 MQTT 主题
0:MQTT 客户端编号
"topic":要订阅的 MQTT 主题
1:MQTT 订阅的 QoS 等级,可以为 0、1 或 2
AT+MQTTSUB=0,"topic",1
# 示例
AT+MQTTSUB=0,"/ikjy4ej7BOp/ESP8266/user/get",1
- 发布指令
${deviceName}
需要替换你的设备名称
MQTTPUB:发布 MQTT 消息
0:MQTT 客户端编号
"topic":要发布的 MQTT 主题
"Json格式内容":要发布的 MQTT 消息内容
1:MQTT 消息的 QoS 等级,可以为 0、1 或 2
0:MQTT 消息的 RETAIN 标志,可以为 0 或 1
AT+MQTTPUB=0,"topic","Json格式内容",1,0
# 示例
AT+MQTTPUB=0,"/ikjy4ej7BOp/ESP8266/user/update","Json格式内容",1,0
其他指令
断开MQTT连接
AT+MQTTCLEAN=0
其他知识
在 MQTT 协议中,发布消息(Publish)指的是客户端向服务器发送消息,而订阅消息(Subscribe)则是客户端接收服务器发送给它的消息
对于 ESP8266 设备而言,如果您需要将设备采集的数据上传至 MQTT 服务器,就需要通过发布消息的方式将数据发送给服务器。而当服务器需要向 ESP8266 发送特定的控制命令或其他数据时,则需要通过订阅 ESP8266 订阅的主题(Topic),向其发送消息
注意/遇到的问题
USART1串口接收需要注意卡死问题,__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE)
不需要开启,两条就够了,还有发送字符串含中文会乱码的情况,可以去Keil勾选C库那个选项,但是还是有警告不用管因为是V6编译,虽然速度快但是对中文还是不太支持,使用V5的话就没那个警告,然后回到vscode重新打开窗口编译即可,也可以重定向printf来发送
OLED显示中文需要把中文字符串所在的 .c 文件设置为 GB2312
编码,不然显示乱码
使用RTC时,我丢滴答定时器里一直闪烁的LED忽然不会闪了一直亮,我以为是代码哪里不小心注释了,结果看了半天没看到,然后想想可能是RTC的问题,果然在MX配置里PC13引脚是RTC相关引脚,通常用于检测备份电源的状态或检测外部事件,如果想控制PC13的话需要在MX里选择输出为无,这样LED正常闪烁了
setting.json
{
"files.associations": {
"my.h": "c",
"tim.h": "c",
"as608.h": "c",
"myall.h": "c",
"buzzer.h": "c",
"key.h": "c",
"led.h": "c",
"oled.h": "c",
"usart.h": "c",
"string": "c",
"string_view": "c",
"array": "c",
"myusart3.h": "c",
"sstream": "c",
"menu.h": "c",
"bmp.h": "c",
"functional": "c",
"algorithm": "c",
"clock.h": "c"
},
"C_Cpp.intelliSenseEngineFallback": "Disabled", //需要添加的
"C_Cpp.intelliSenseEngine": "Tag Parser", // 需要添加的
}
launch.json
{
"name": "Your Debug Configuration",
"type": "cortex-debug",
"request": "launch",
"servertype": "keil",
"preLaunchTask": "build",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/MDK-ARM/Fingerprint_Access_Control/Fingerprint_Access_Control.axf",
"ignore": ["WarningNumber1", "WarningNumber2","-Winvalid-source-encoding"]
}
喂狗需要注意不要太接近重装载值,因为RC温漂大可能会导致复位
如果WIFI设置模式或者发送AT都返回错误,可以试试把WiFi模块的3.3v重新拔了再插回去(不要只按单片机复位,一定要断电WiFI模块)
供电最好使用USB口供电,如果只使用下载程序那供电会出现电压不足,导致OLED有乱码,舵机不会动等问题
待解决
指纹的背光问题还是找不到解决方法,一直亮着太闪眼了,理想是按下才亮,不按下指纹处于灭状态
参考过的文章:https://www.arduino.cn/forum.php?mod=viewthread&tid=105344&page=1#pid622605
结语
这次搞这个也学到了很多,知道了怎么去烧写wifi固件,只不过遗憾的是搞MQTT连接阿里云那里一直不太行,不知道哪里有问题,这个问题暂时先这样了只能,等后面准备再搞一个物联网的小项目做做