完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
F407ZG最小系统
作者本人采用的是正点原子的STM32F4ZGT6带SRAM版。因为带SRAM没有片选SRAM使用的IO口所以选用几个独立的IO口进行操作。选取IO口的方法为先查看数据手册还有正点原子增值资料中的IO口表格文档,查找适合本次实验所需的IO口。 例如我需要一个USART3的串口进行蓝牙通信,我在芯片手册中查找USART3_TX/RX这两个IO口,查找到之后去IO口表格文档中查找这两个引脚对应的连接属性,经本次操作可知,USART3_TX/RX两个口完全独立并且IO口已引出,可以进行外设连接操作。 其中相应的数据手册和IO口表格,可根据不同开发板提供的资料进行查找。 矩阵键盘 作者本人采用的是最普通的4*4矩阵键盘,某宝几块钱就可以获取的。 线路连接:(上四IO口为列,下四IO口为行,表格以从下到上说明) IO选择原因:此次程序操作了IDR和ODR寄存器,如此选择IO口,可方便寄存器的操作,以便实现代码。 线路连接列表
矩阵键盘的操作原理:上四列IO口设置为输入模式,下四行IO口设置为输出模式。由原理图可知当按下其中一个键时,其中两个IO口短接。输入一端的IO口读取到了输出一段的IO口的电平,单片机会将这一采取结果返回给程序进行判断。具体操作与代码流程如下。 1.写44KEY头文件44KEY.h的代码,其中包含两个函数一个GPIO初始化函数一个按键操作函数。 #ifndef _44KEY_H #define _44KEY_H #include "sys.h" void gpio_init_key4(void);//矩阵键盘引脚初始化 u16 key_init_44(void);//4*4矩阵键盘函数(单次按键模式) #endif 2.写44KEY.c中gpio_init_key4的函数,此函数的目的是为了初始化矩阵键盘相关的GPIO。正如本小结开头所说,其中PC的0、1、2、3设置成为了推挽输出模式,其余的PC4、5、PG6、7设置成为了输入模式。 GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOG, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉 GPIO_Init(GPIOG,&GPIO_InitStructure);//初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉 GPIO_Init(GPIOC,&GPIO_InitStructure);//初始化 3.写44KEY.c中key_init_44的函数,此函数的目的是为了读取相关的IO口,并进行判断是哪一个按键按下的,使用的方法为扫描法。以按下S1按键为例(第一行第一列的按键)进行第一行的扫描,由于没有按键按下时列输入IO读取到的值为初始的全高(IO引脚悬空时默认为高),以此为方法进行判断。令第一行输出低电平,如果某处按键按下则对应列输入信号为低电平,其余因为没有按键按下造成短路所以依旧为高电平(IO引脚悬空时默认为高)。此时PC0、1、2、3、4、5和PG6、7的GPIO的值为1110 0111,在寄存器储存值为0xE7,则判断成功为S1按键按下。 8个IO口的储存=(GPIOC->IDR&0x37)|(GPIOG->IDR&0xC0); GPIO_SetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2); OLED显示 作者本人采用的是某宝较为便宜的0.96寸7针OLED显示使用的是SPI通信协议的。 线路连接:
OLED的原理:根据所给数据的16进制数进行点亮和熄灭,例如逐列式顺式第一列点阵(一列16个点),如果给了一个16进制数为0x00ff,则表示前8个点不点亮后8个点点亮。 取字模的方法:使用PCtoLCD2002.exe取模软件进行文字取模,主要设置如下阴码、列行式、逆向。格式处为C51的格式,其余可以不做改动。过程如下图所示。(主要生成16*16的字模,其余大小字体方法类似) 字模生成以后将其复制到主程序字库中,作者本人字库是放在oledfone.h头文件中定义的,其中如果字体数量超过了你能保存的大小则需要修改数组的大小。如下图所示,我定义了一个密字,则将代码复制于此。此字库中一共保存了50个字(每个字需要两行16进制组成,则每两个数组保存一个字)每个字的生成大小为16*16。 取图片模的方法:使用PCtoLCD2002.exe取模软件进行图片取模,首先需要先切换到图片模式(取模软件处点击模式,选择图片模式)。 在图片取模之前需要先对图片进行处理,首先选择一张颜色单一的图片(也就是只有深色和白色的但不能包含浅色的图片,不然识别不出来图片的质量会下降)以画图的方式打开选择重新调整大小,调整大小范围在长128宽64之内(作者本人使用的是128*64的OLED屏幕,如果其他屏幕可更改此处设置)。然后就是保存图片,保存图片的方法为点击另存为选择BMP类图片保存,然后在选择为单色图。如图所示。 之后就是将图片在PCtoLCD2002.exe取模软件中打开进行图片取模,跟文字取模一样需要对取模方式进行一些设置,其中设置阴码、列行式、逆向,格式为C51格式并且去除两个大括号。如图所示。 最后就是将取模成功后的图片放置到程序中,我保存在bmp.h的头文件中,方法与取字模方法相同。如图所示。 驱动程序保存在程序总代码中,如有需要请自行提取。 舵机 作者本人使用的是TD-7015MG舵机,因为手头只有这一种舵机。 线路连接:
由舵机转动的原理,我们将频率设置为50HZ即0.002秒一个周期(使用定时器),我们设置的脉冲为0.0019时为顺时针转动,设置的脉冲为0.0018时为逆时针转动,符合我们开关门锁的要求。具体代码操作如下。 //进行结构体声明 GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3); //GPIO初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOC,&GPIO_InitStructure); //定时器计时初始化 TIM_TimeBaseStructure.TIM_Prescaler=8399; TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period=200; TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); //输出PWM设置 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; TIM_OC1Init(TIM3, &TIM_OCInitStructure); //使能 TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3,ENABLE); TIM_Cmd(TIM3, ENABLE); HC-05蓝牙模块 本文作者使用的时某宝购买的HC-05蓝牙模块,如下图所示。 原理:我们使用蓝牙模块当作媒介来使用单片机的某个串口进行串口通信。 线路连接:(因为只使用建议的蓝牙从模式所以STATE和EN接口未连接) 蓝牙与单片机
蓝牙模块常用指令集
进入AT模式之后,打开XCOM.exe,进行波特率为38400和串口的设置。 逐次发送:AT、AT+NAME=BEIJING、AT+ROLE=0、AT+PSWD=1234、AT+UART=115200,0,0命令进行蓝牙模块作为从设备的设置 蓝牙模块与手机连接、代码分析 在蓝牙模块设置好模式之后,使用手机随便打开一个蓝牙串口助手,等待搜索到蓝牙并进行连接。串口和定时器的初始化,不再过多的赘述,都是简单的配置 以下是串口3的初始化和中断服务函数 void usart3_init(u32 bound) { NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; USART_DeInit(USART3); //复位串口3 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//使能USART3时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10; //GPIOB11和GPIOB10初始化 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIOB11,和GPIOB10 GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3); //GPIOB11复用为USART3 GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3); //GPIOB10复用为USART3 USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART3, &USART_InitStructure); //初始化串口3 USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启中断 USART_Cmd(USART3, ENABLE); //使能串口 NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 TIM7_Int_Init(100-1,8400-1); //10ms中断一次 TIM_Cmd(TIM7, DISABLE); //关闭定时器7 USART3_RX_STA=0; //清零 } void USART3_IRQHandler(void) { u8 res; if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)//接收到数据 { res =USART_ReceiveData(USART3); if((USART3_RX_STA&(1<<15))==0)//接收完的一批数据,还没有被处理,则不再接收其他数据 { if(USART3_RX_STA TIM_SetCounter(TIM7,0);//计数器清空 if(USART3_RX_STA==0) TIM_Cmd(TIM7, ENABLE); //使能定时器7 USART3_RX_BUF[USART3_RX_STA++]=res; //记录接收到的值 }else { USART3_RX_STA|=1<<15; //强制标记接收完成 } } } } 定时器的初始化 void TIM7_Int_Init(u16 arr,u16 psc) { NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能 //定时器TIM7初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断 TIM_Cmd(TIM7,ENABLE);//使能定时器7 NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 } void TIM7_IRQHandler(void) { if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断 { USART3_RX_STA|=1<<15; //标记接收完成 TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中断标志 TIM_Cmd(TIM7, DISABLE); //关闭TIM7 } } 连线说明 各模块与单片机的连接合理,并且不影响单片机其他的功能。 矩阵键盘的连线:
OLED屏幕的连线:
|
||||||||
|
||||||||
代码解析
44KEY.C代码:按照之前对4*4矩阵键盘讲解的扫描模式,完整的程序代码。其中使用了单次按键的方法,防止长按KEY产生的错误影响。 u16 key_init_44(void)//4*4矩阵键盘函数(单次按键模式) { static u8 t=1; u16 Key_val=0; u32 temp=0; gpio_init_key4(); if(t) { GPIO_ResetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); GPIO_SetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2); GPIO_ResetBits(GPIOC,GPIO_Pin_3); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { delay_ms(10); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { temp=(GPIOC->IDR&0x37)|(GPIOG->IDR&0xC0); switch(temp) { case 0xE7:Key_val=1; break; case 0xD7:Key_val=2; break; case 0xB7:Key_val=3; break; case 0x77:Key_val=4; break; default :Key_val=0; break; } } } GPIO_ResetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); GPIO_SetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_3); GPIO_ResetBits(GPIOC,GPIO_Pin_2); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { delay_ms(10); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { temp=(GPIOC->IDR&0x3B)|(GPIOG->IDR&0xC0); switch(temp) { case 0xEB:Key_val=5; break; case 0xDB:Key_val=6; break; case 0xBB:Key_val=7; break; case 0x7B:Key_val=8; break; default :Key_val=0; break; } } } GPIO_ResetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); GPIO_SetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_3|GPIO_Pin_2); GPIO_ResetBits(GPIOC,GPIO_Pin_1); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { delay_ms(10); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { temp=(GPIOC->IDR&0x3D)|(GPIOG->IDR&0xC0); switch(temp) { case 0xED:Key_val=9; break; case 0xDD:Key_val=10; break; case 0xBD:Key_val=11; break; case 0x7D:Key_val=12; break; default :Key_val=0; break; } } } GPIO_ResetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); GPIO_SetBits(GPIOC,GPIO_Pin_3|GPIO_Pin_1|GPIO_Pin_2); GPIO_ResetBits(GPIOC,GPIO_Pin_0); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { delay_ms(10); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { temp=(GPIOC->IDR&0x3E)|(GPIOG->IDR&0xC0); switch(temp) { case 0xEE:Key_val=13; break; case 0xDE:Key_val=14; break; case 0xBE:Key_val=15; break; case 0x7E:Key_val=16; break; default :Key_val=0; break; } } } if(Key_val!=0) { t=0; } return Key_val; } else if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))==0xF0) { t=1; } return 0; } OLED.C代码:OLED驱动显示屏部分 u8 OLED_GRAM[144][8]; //反显函数 void OLED_ColorTurn(u8 i) { if(i==0) { OLED_WR_Byte(0xA6,OLED_CMD);//正常显示 } if(i==1) { OLED_WR_Byte(0xA7,OLED_CMD);//反色显示 } } //屏幕旋转180度 void OLED_DisplayTurn(u8 i) { if(i==0) { OLED_WR_Byte(0xC8,OLED_CMD);//正常显示 OLED_WR_Byte(0xA1,OLED_CMD); } if(i==1) { OLED_WR_Byte(0xC0,OLED_CMD);//反转显示 OLED_WR_Byte(0xA0,OLED_CMD); } } void OLED_WR_Byte(u8 dat,u8 cmd) { u8 i; if(cmd) OLED_DC_Set(); else OLED_DC_Clr(); OLED_CS_Clr(); for(i=0;i<8;i++) { OLED_SCLK_Clr(); if(dat&0x80) OLED_SDIN_Set(); else OLED_SDIN_Clr(); OLED_SCLK_Set(); dat<<=1; } OLED_CS_Set(); OLED_DC_Set(); } //开启OLED显示 void OLED_DisPlay_On(void) { OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能 OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵 OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕 } //关闭OLED显示 void OLED_DisPlay_Off(void) { OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能 OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵 OLED_WR_Byte(0xAF,OLED_CMD);//关闭屏幕 } //更新显存到OLED void OLED_Refresh(void) { u8 i,n; for(i=0;i<8;i++) { OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址 OLED_WR_Byte(0x00,OLED_CMD); //设置低列起始地址 OLED_WR_Byte(0x10,OLED_CMD); //设置高列起始地址 for(n=0;n<128;n++) OLED_WR_Byte(OLED_GRAM[n],OLED_DATA); } } //清屏函数 void OLED_Clear(void) { u8 i,n; for(i=0;i<8;i++) { for(n=0;n<128;n++) { OLED_GRAM[n]=0;//清除所有数据 } } OLED_Refresh();//更新显示 } //画点 //x:0~127 //y:0~63 void OLED_DrawPoint(u8 x,u8 y) { u8 i,m,n; i=y/8; m=y%8; n=1< } //清除一个点 //x:0~127 //y:0~63 void OLED_ClearPoint(u8 x,u8 y) { u8 i,m,n; i=y/8; m=y%8; n=1< OLED_GRAM[x]|=n; OLED_GRAM[x]=~OLED_GRAM[x]; } //画线 //x:0~128 //y:0~64 void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2) { u8 i,k,k1,k2,y0; if((x1<0)||(x2>128)||(y1<0)||(y2>64)||(x1>x2)||(y1>y2))return; if(x1==x2) //画竖线 { for(i=0;i<(y2-y1);i++) { OLED_DrawPoint(x1,y1+i); } } else if(y1==y2) //画横线 { for(i=0;i<(x2-x1);i++) { OLED_DrawPoint(x1+i,y1); } } else //画斜线 { k1=y2-y1; k2=x2-x1; k=k1*10/k2; for(i=0;i<(x2-x1);i++) { OLED_DrawPoint(x1+i,y1+i*k/10); } } } //x,y:圆心坐标 //r:圆的半径 void OLED_DrawCircle(u8 x,u8 y,u8 r) { int a, b,num; a = 0; b = r; while(2 * b * b >= r * r) { OLED_DrawPoint(x + a, y - b); OLED_DrawPoint(x - a, y - b); OLED_DrawPoint(x - a, y + b); OLED_DrawPoint(x + a, y + b); OLED_DrawPoint(x + b, y + a); OLED_DrawPoint(x + b, y - a); OLED_DrawPoint(x - b, y - a); OLED_DrawPoint(x - b, y + a); a++; num = (a * a + b * b) - r*r;//计算画的点离圆心的距离 if(num > 0) { b--; a--; } } } //在指定位置显示一个字符,包括部分字符 //x:0~127 //y:0~63 //size:选择字体 12/16/24 //取模方式 逐列式 void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1) { u8 i,m,temp,size2,chr1; u8 y0=y; size2=(size1/8+((size1%8)?1:0))*(size1/2); //得到字体一个字符对应点阵集所占的字节数 chr1=chr-' '; //计算偏移后的值 for(i=0;i if(size1==12) {temp=asc2_1206[chr1];} //调用1206字体 else if(size1==16) {temp=asc2_1608[chr1];} //调用1608字体 else if(size1==24) {temp=asc2_2412[chr1];} //调用2412字体 else return; for(m=0;m<8;m++) //写入数据 { if(temp&0x80)OLED_DrawPoint(x,y); else OLED_ClearPoint(x,y); temp<<=1; y++; if((y-y0)==size1) { y=y0; x++; break; } } } } //显示字符串 //x,y:起点坐标 //size1:字体大小 //*chr:字符串起始地址 void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1) { while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符! { OLED_ShowChar(x,y,*chr,size1); x+=size1/2; if(x>128-size1) //换行 { x=0; y+=2; } chr++; } } //m^n u32 OLED_Pow(u8 m,u8 n) { u32 result=1; while(n--) { result*=m; } return result; } 显示2个数字 x,y :起点坐标 len :数字的位数 size:字体大小 void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1) { u8 t,temp; for(t=0;t temp=(num/OLED_Pow(10,len-t-1))%10; if(temp==0) { OLED_ShowChar(x+(size1/2)*t,y,'0',size1); } else { OLED_ShowChar(x+(size1/2)*t,y,temp+'0',size1); } } } //显示汉字 //x,y:起点坐标 //num:汉字对应的序号 //取模方式 列行式 void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1) { u8 i,m,n=0,temp,chr1; u8 x0=x,y0=y; u8 size3=size1/8; while(size3--) { chr1=num*size1/8+n; n++; for(i=0;i if(size1==16) {temp=Hzk1[chr1];}//调用16*16字体 else if(size1==24) {temp=Hzk2[chr1];}//调用24*24字体 else if(size1==32) {temp=Hzk3[chr1];}//调用32*32字体 else if(size1==64) {temp=Hzk4[chr1];}//调用64*64字体 else return; for(m=0;m<8;m++) { if(temp&0x01)OLED_DrawPoint(x,y); else OLED_ClearPoint(x,y); temp>>=1; y++; } x++; if((x-x0)==size1) {x=x0;y0=y0+8;} y=y0; } } } //num 显示汉字的个数 //space 每一遍显示的间隔 void OLED_ScrollDisplay(u8 num,u8 space) { u8 i,n,t=0,m=0,r; while(1) { if(m==0) { OLED_ShowChinese(128,24,t,16); //写入一个汉字保存在OLED_GRAM[][]数组中 t++; } if(t==num) { for(r=0;r<16*space;r++) //显示间隔 { for(i=0;i<144;i++) { for(n=0;n<8;n++) { OLED_GRAM[i-1][n]=OLED_GRAM[n]; } } OLED_Refresh(); } t=0; } m++; if(m==16){m=0;} for(i=0;i<144;i++) //实现左移 { for(n=0;n<8;n++) { OLED_GRAM[i-1][n]=OLED_GRAM[n]; } } OLED_Refresh(); } } //配置写入数据的起始位置 void OLED_WR_BP(u8 x,u8 y) { OLED_WR_Byte(0xb0+y,OLED_CMD);//设置行起始地址 OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD); OLED_WR_Byte((x&0x0f),OLED_CMD); } //x0,y0:起点坐标 //x1,y1:终点坐标 //BMP[]:要写入的图片数组 void OLED_ShowPicture(u8 x0,u8 y0,u8 x1,u8 y1,u8 BMP[]) { u32 j=0; u8 x=0,y=0; if(y%8==0)y=0; else y+=1; for(y=y0;y OLED_WR_BP(x0,y); for(x=x0;x OLED_WR_Byte(BMP[j],OLED_DATA); j++; } } } |
|
|
|
PWM.C代码:输出一个20ms为周期的PWM波,定时器产生PWM波形计算方法:84000000/(8399+1)*(199+1)=0.02。
void TIM3_PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOC,&GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Prescaler=8399; TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period=199; TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; TIM_OC1Init(TIM3, &TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3,ENABLE); TIM_Cmd(TIM3, ENABLE); } USART3.C代码:进行串口的初始化,并使能相应的接收中断函数,来达到可接收函数解锁的目的。 void usart3_init(u32 bound) { NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; USART_DeInit(USART3); //复位串口3 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//使能USART3时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10; //GPIOB11和GPIOB10初始化 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIOB11,和GPIOB10 GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3); //GPIOB11复用为USART3 GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3); //GPIOB10复用为USART3 USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART3, &USART_InitStructure); //初始化串口3 USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启中断 USART_Cmd(USART3, ENABLE); //使能串口 NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 TIM7_Int_Init(100-1,8400-1); //10ms中断一次 TIM_Cmd(TIM7, DISABLE); //关闭定时器7 USART3_RX_STA=0; //清零 } void USART3_IRQHandler(void) { u8 res; if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)//接收到数据 { res =USART_ReceiveData(USART3); if((USART3_RX_STA&(1<<15))==0)//接收完的一批数据,还没有被处理,则不再接收其他数据 { if(USART3_RX_STA TIM_SetCounter(TIM7,0);//计数器清空 if(USART3_RX_STA==0) TIM_Cmd(TIM7, ENABLE); //使能定时器7 USART3_RX_BUF[USART3_RX_STA++]=res; //记录接收到的值 }else { USART3_RX_STA|=1<<15; //强制标记接收完成 } } } } TIMER.C代码:USART3的定时器设置。 void TIM7_Int_Init(u16 arr,u16 psc) { NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能 //定时器TIM7初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断 TIM_Cmd(TIM7,ENABLE);//使能定时器7 NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 } void TIM7_IRQHandler(void) { if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断 { USART3_RX_STA|=1<<15; //标记接收完成 TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中断标志 TIM_Cmd(TIM7, DISABLE); //关闭TIM7 } } GUI.C代码:通过调用OLED中的驱动函数设计出了一下OLED画面。 void GUI_OLED_LOCK(void)//锁屏面板初始化 { OLED_Clear(); OLED_ShowChinese(48,0,13,16); OLED_ShowChinese(64,0,36,16); OLED_ShowChinese(0,16,0,16); OLED_ShowChinese(16,16,1,16); OLED_ShowString(32,16,":",16);//密码开始打印位置为x=40,y=16 OLED_Refresh(); OLED_ShowPicture(48,4,80,8,BMP1); } void GUI_OLED_UNLOCHING(void)//进行开锁中 { u8 i; double t=0.0; char buf[8]; OLED_Clear(); OLED_ShowChinese(32,0,2,16); OLED_ShowChinese(48,0,7,16); OLED_ShowChinese(64,0,19,16); OLED_ShowChinese(80,0,13,16); OLED_ShowChinese(0,16,29,16); OLED_ShowChinese(16,16,30,16); OLED_Refresh(); for(i=0;i<80;i++) { t=t+1.25; sprintf((char *)buf,"%3.0f%%",t); OLED_DrawLine(i+32,16,i+32,32); OLED_ShowString(88,48,(uint8_t *)buf,16); OLED_Refresh(); OLED_ShowPicture(48,4,80,8,BMP1); delay_ms(50); } } void GUI_OLED_UNLOCK_OK(void)//开锁成功 { u8 i; double t=0.0; char buf[8]; OLED_Clear(); OLED_ShowChinese(32,0,19,16); OLED_ShowChinese(48,0,13,16); OLED_ShowChinese(64,0,10,16); OLED_ShowChinese(80,0,11,16); OLED_ShowChinese(0,16,31,16); OLED_ShowChinese(16,16,32,16); OLED_Refresh(); for(i=0;i<80;i++) { t=t+1.25; sprintf((char *)buf,"%3.0f%%",t); OLED_DrawLine(i+32,16,i+32,32); OLED_ShowString(88,48,(uint8_t *)buf,16); OLED_Refresh(); OLED_ShowPicture(43,4,85,8,BMP2); delay_ms(50); } } void GUI_OLED_UNLOCK_NO(void)//开锁失败 { u8 i; double t=0.0; char buf[8]; OLED_Clear(); OLED_ShowChinese(32,0,19,16); OLED_ShowChinese(48,0,13,16); OLED_ShowChinese(64,0,8,16); OLED_ShowChinese(80,0,9,16); OLED_ShowChinese(0,16,25,16); OLED_ShowChinese(16,16,33,16); OLED_Refresh(); for(i=0;i<80;i++) { t=t+1.25; sprintf((char *)buf,"%3.0f%%",t); OLED_DrawLine(i+32,16,i+32,32); OLED_ShowString(88,48,(uint8_t *)buf,16); OLED_Refresh(); OLED_ShowPicture(48,4,80,8,BMP1); delay_ms(50); } } void GUI_OLED_UNLOCK(void)//开锁成功返回操控界面 { OLED_Clear(); OLED_ShowChinese(32,0,14,16); OLED_ShowChinese(48,0,15,16); OLED_ShowChinese(64,0,16,16); OLED_ShowChinese(80,0,17,16); OLED_ShowChinese(0,16,37,16); OLED_ShowChinese(16,16,38,16); OLED_ShowChinese(32,16,39,16); OLED_ShowChinese(48,16,40,16); OLED_ShowString(64,16,":",16); OLED_ShowString(0,32,"*:",16); OLED_ShowChinese(16,32,34,16); OLED_ShowChinese(32,32,35,16); OLED_ShowChinese(48,32,0,16); OLED_ShowChinese(64,32,1,16); OLED_ShowString(0,48,"#:",16); OLED_ShowChinese(16,48,13,16); OLED_ShowChinese(32,48,36,16); OLED_Refresh(); } void GUI_PSSKEY(void)//按键说明界面 { OLED_Clear(); OLED_ShowNum(8,0,1,1,16); OLED_ShowNum(40,0,2,1,16); OLED_ShowNum(72,0,3,1,16); OLED_ShowChinese(96,0,41,16); OLED_ShowChinese(112,0,42,16); OLED_ShowNum(8,16,4,1,16); OLED_ShowNum(40,16,5,1,16); OLED_ShowNum(72,16,6,1,16); OLED_ShowChinese(96,16,43,16); OLED_ShowChinese(112,16,42,16); OLED_ShowNum(8,32,7,1,16); OLED_ShowNum(40,32,8,1,16); OLED_ShowNum(72,32,9,1,16); OLED_ShowChinese(96,32,13,16); OLED_ShowChinese(112,32,36,16); OLED_ShowString(8,48,"*",16); OLED_ShowNum(40,48,0,1,16); OLED_ShowString(72,48,"#",16); OLED_ShowChinese(96,48,3,16); OLED_ShowChinese(112,48,44,16); OLED_Refresh(); } void GUI_PCHANGE(void)//密码修改界面 { OLED_Clear(); OLED_ShowChinese(32,0,45,16); OLED_ShowChinese(48,0,35,16); OLED_ShowChinese(64,0,0,16); OLED_ShowChinese(80,0,1,16); OLED_ShowChinese(0,16,37,16); OLED_ShowChinese(16,16,38,16); OLED_ShowChinese(32,16,39,16); OLED_ShowChinese(48,16,40,16); OLED_ShowString(64,16,":",16); OLED_ShowChinese(0,32,26,16); OLED_ShowChinese(16,32,0,16); OLED_ShowChinese(32,32,1,16); OLED_ShowString(48,32,":",16); OLED_ShowChinese(96,48,3,16); OLED_ShowChinese(112,48,44,16); OLED_Refresh(); } MAIN.C代码:(修改密码系统部分) 思路:进入一次while函数进行一个死循环,只有当按键按下时才进行下面的操作。在下面的操作当中,合理的运用if和else if设置一个按键优先级顺序,按照这样的优先级顺序来进行按键操作。运用goto函数来进行每次按键回反的操作。 while((key4_val3=key_init_44())==RESET); if(key4_val3==13)//按下*即S13键进行修改密码 { GUI_PCHANGE(); for(i=0;i<7;i++) { delay_ms(200); while((key4_val4=key_init_44())==RESET); if(key4_val4==13)//返回系统主界面 { goto loop2; } else if(key4_val4==4)//删除 { i=-1; for(j=0;j<6;j++) { keysd[j]=j+1; } key4_val4=0; GUI_PCHANGE(); } else if(key4_val4==8)//清除 { if(i>0) { i--; keysd=i+1; OLED_ShowString((56+i*8),32," ",16); OLED_Refresh(); i--; } key4_val4=0; } else if(key4_val4==16)//确认 { if(i==6) { for(i=0;i<6;i++) { keysd=keysa; } OLED_ShowChinese(0,48,10,16); OLED_ShowChinese(16,48,11,16); OLED_Refresh(); goto loop2; } key4_val4=0; } else if((key4_val4!=15)&&(key4_val4!=12))//数字输入 { u8 temp; if(i<6) { switch(key4_val4) { case 1:temp=1; break; case 2:temp=2; break; case 3:temp=3; break; case 5:temp=4; break; case 6:temp=5; break; case 7:temp=6; break; case 9:temp=7; break; case 10:temp=8; break; case 11:temp=9; break; case 14:temp=0; break; default:temp=10; break; } keysa=temp; OLED_ShowString((56+i*8),32,"*",16); OLED_Refresh(); } else { i--; } } else { i--; } key4_val4=0; } } else if(key4_val3==15)//按下#即S15键进行系统锁定 { key4_val3=0; key4_val2=0; TIM_SetCompare1(TIM3,180); delay_ms(2000); TIM_SetCompare1(TIM3,100); goto loop1; } |
|
|
|
只有小组成员才能发言,加入小组>>
3278 浏览 9 评论
2955 浏览 16 评论
3455 浏览 1 评论
8987 浏览 16 评论
4050 浏览 18 评论
1102浏览 3评论
570浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
568浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2301浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1857浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 04:27 , Processed in 1.120729 second(s), Total 83, Slave 63 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号