基于ch32开发的智能门锁第三讲(模块化处理,矩阵键盘)-零基础教程
·
模块化处理
原因:将所有的代码都放入一个main中会显得繁冗所以进行模块化处理(推荐后续移植的模块统一都做这个处理,这样代码的易读性增加)
重点说明:
1.如果遇见了没有找到某文件的情况,请按上一章添加Driver文件夹一样将文件进行添加
2.本次的基础是以上一个文件进行开发的所以无需创建新工程
模块化处理步骤
-
右键选择工程右键进行创建头文件和源文件(timer.c和.h)
-
新建后将上一讲的GPIO初始化与定时器初始化放置到一起->在主函数中对这个文件进行声明(#include "timer.h"//定时器头文件)
/* * timer.c * * Created on: 2025年9月13日 * Author: leader */ #include "debug.h" void Tim3_Init(u16 arr,u16 psc)//arr-自动重装载的值 psc-分频值 { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;//定义结构体 NVIC_InitTypeDef NVIC_InitStructure;//定义结构体 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//打开TIM3的时钟(注意该时钟挂载的总线是哪一个APB1/APB2)TIM3-APB1 TIM_TimeBaseInitStruct.TIM_ClockDivision=0;//时钟分割 TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//设置为向上计数 TIM_TimeBaseInitStruct.TIM_Period=arr;//自动重装载值,5000为500ms TIM_TimeBaseInitStruct.TIM_Prescaler=psc;//设置分频值 TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct); TIM_ITConfig(TIM3, TIM_IT_Update|TIM_IT_Trigger, ENABLE); NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;//需要记得使用拥有中断通道的时钟 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM3, ENABLE); } // 定义舵机的PWM周期和占空比 #define PWM_PERIOD 20000 // PWM周期为20ms (20000us) #define PWM_MIN 500 // 最小脉宽为0.5ms (500us) #define PWM_MAX 2500 // 最大脉宽为2.5ms (2500us) // 初始化TIM2以生成PWM信号 void TIM2_PWM_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能TIM2时钟 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 配置TIM2的基本参数 TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD - 1; // 设置自动重装值 TIM_TimeBaseStructure.TIM_Prescaler = 96 - 1; // 设置预分频器,96MHz/96 = 1MHz (1us) TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 配置TIM2的PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = PWM_MIN; // 初始占空比为最小值 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 配置TIM2通道1 TIM_OC1Init(TIM2, &TIM_OCInitStructure); // 使用TIM2的通道1 TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); // 使能TIM2通道1的预装载寄存器 TIM_ARRPreloadConfig(TIM2, ENABLE); // 使能TIM2的自动重装寄存器预装载 TIM_Cmd(TIM2, ENABLE); // 使能TIM2 //初始化GPIO以输出PWM信号-这里我将定时器2的初始化与它GPIO的初始化放在一起整合为1个函数 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟 GPIO_InitTypeDef GPIO_InitStructure; // 配置PA0为复用推挽输出(TIM2通道1) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); } void lock(unsigned char mode)//舵机的工作模式0-上锁 1-开锁 { if(mode==0)//0度 { TIM_SetCompare1(TIM2, 500);//设置占空比 }else if(mode==1) { TIM_SetCompare1(TIM2, 1500);//设置占空比 } }#ifndef DRIVER_TIMER_H_ #define DRIVER_TIMER_H_ void Tim3_Init(u16 arr,u16 psc); void TIM2_PWM_Init(void); void lock(unsigned char mode); #endif /* DRIVER_TIMER_H_ */ -
对应主函数中可以讲GPIO的初始化进行删除,同时将原来对定时器的设置变为使用封装的lock函数进行控制舵机的初始状态
int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级分组 SystemCoreClockUpdate();//时钟配置 //USART_Printf_Init(115200);//串口1初始化 Delay_Init();//延时函数初始化 TIM2_PWM_Init();//定时器初始化 lock(1);//上锁 LCD_Init();//LCD初始化 LCD_Fill(0, 0, 127, 127, WHITE);//填充屏幕 LCD_ShowPicture(0, 0, 128, 128, gImage_1);//展示照片 unsigned char i=0; while(i<128) { LCD_DrawLine(i, 0, i, 10, RED);//绘制一条红色的随时间增长的红线(可以制作为进度条) i++; Delay_Ms(20); } /*进度条走完后,首页显示*/ LCD_ShowPicture(0, 0, 128, 128, gImage_2); LCD_Show_Chinese(0, 0, "门锁状态上锁", RED, WHITE, 16, 0); LCD_Show_Chinese(0,30,"输入密码",RED,WHITE,16,0); while(1) { } } -
封装LCD_Show_Chinese函数原来LCD_ShowChinese只能一个一个进行显示过于麻烦
void LCD_Show_Chinese(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode) { while(*s) { LCD_ShowChinese(x,y,s,fc,bc,sizey,mode); x+=16; if(x>127){x=0; y+=16;}//换行 s+=2;//由于我们使用的是汉字(2个字节)的字符串所以这个地方+=2 } }
矩阵键盘的移植(注意:所有的GPIO都是挂载在APB2总线上)
接线说明:前4个引脚配置为Out_PP后4个引脚配置为IPU
R4-PD11 R3-PD9 R2-PE15 R1-PE13 C1-PE11 C2-PE9 C3-PE7 C4-PC5(这个是根据我买的矩阵键盘为例,如果大家是自己焊接的可以根据自己的需求进行DIY)

移植步骤
-
创建key.c和.h
-
将我们配置的LED的GPIO的代码进行移植并修改可以得到-key_init()按键初始化的书写
void key_init() { //初始化GPIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); // 使能GPIOD时钟 GPIO_InitTypeDef GPIO_InitStructure; // 配置PA0为复用推挽输出(TIM2通道1) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOD, &GPIO_InitStructure); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); // 使能GPIOD时钟 // 配置PA0为复用推挽输出(TIM2通道1) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15|GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStructure); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); // 使能GPIOD时钟 // 配置PA0为复用推挽输出(TIM2通道1) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_9|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStructure); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // 使能GPIOD时钟 // 配置PA0为复用推挽输出(TIM2通道1) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); } -
按键读取函数的书写-key_read
u8 key_read() { u8 temp=0; //R4 GPIO_ResetBits(GPIOD, GPIO_Pin_11);GPIO_SetBits(GPIOD, GPIO_Pin_9);GPIO_SetBits(GPIOE, GPIO_Pin_15); GPIO_SetBits(GPIOE, GPIO_Pin_13);//设置首行为低电平然后列扫描(后续同上) if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_11)==0)temp=4; if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_9)==0)temp=3; if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_7)==0)temp=2; if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_5)==0)temp=1;//C4 //R3 GPIO_SetBits(GPIOD, GPIO_Pin_11);GPIO_ResetBits(GPIOD, GPIO_Pin_9);GPIO_SetBits(GPIOE, GPIO_Pin_15); GPIO_SetBits(GPIOE, GPIO_Pin_13); if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_11)==0)temp=8;//C1 if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_9)==0)temp=7;//C2 if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_7)==0)temp=6;//C3 if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_5)==0)temp=5;//C4 //R2 GPIO_SetBits(GPIOD, GPIO_Pin_11);GPIO_SetBits(GPIOD, GPIO_Pin_9);GPIO_ResetBits(GPIOE, GPIO_Pin_15); GPIO_SetBits(GPIOE, GPIO_Pin_13); if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_11)==0)temp=12; if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_9)==0)temp=11; if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_7)==0)temp=10; if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_5)==0)temp=9; //R1 GPIO_SetBits(GPIOD, GPIO_Pin_11);GPIO_SetBits(GPIOD, GPIO_Pin_9);GPIO_SetBits(GPIOE, GPIO_Pin_15); GPIO_ResetBits(GPIOE, GPIO_Pin_13); if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_11)==0)temp=16; if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_9)==0)temp=15; if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_7)==0)temp=14; if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_5)==0)temp=13; return temp; } -
key.h文件的书写
#ifndef DRIVER_KEY_H_ #define DRIVER_KEY_H_ void key_init(); u8 key_read(); #endif /* DRIVER_KEY_H_ */ -
在main文件中对这个key.h进行引用
-
主函数的书写
int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级分组 SystemCoreClockUpdate();//时钟配置 //USART_Printf_Init(115200);//串口1初始化 Delay_Init();//延时函数初始化 TIM2_PWM_Init();//定时器初始化 GPIO_Pwm_Init();//GPIO初始化 TIM_SetCompare1(TIM2, 1500);//设置占空比 LCD_Init();//LCD初始化 LCD_Fill(0, 0, 127, 127, WHITE); LCD_ShowPicture(0, 0, 128, 128, gImage_1); unsigned char i=0; while(i<128) { LCD_DrawLine(i, 0, i, 10, RED);//绘制一条红色的随时间增长的红线(可以制作为进度条) i++; Delay_Ms(20); } /*进度条走完后,首页显示*/ LCD_ShowPicture(0, 0, 128, 128, gImage_2); LCD_Show_Chinese(0, 0, "门锁状态:上锁", RED, WHITE, 16, 0); LCD_Show_Chinese(0,30,"输入密码",RED,WHITE,16,0); key_init(); while(1) { num = key_read();//获取当前按键的数字 LCD_ShowIntNum(0, 0, num, 3, RED, WHITE, 16); } }
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)