赞
踩
最近一直在肝STM32系列芯片,这里我想要检验一下自己的初步学习成果,简单的运用一下IO口的配置,代码使用正点的库函数模板写的,仿真软件是Proteus 8.13,代码编写编译软件是Keil MDK5,仿真芯片是STM32F103R6小容量芯片。
目录
stm32f103R6芯片的IO口中以下为硬件的具体配置
PA9、PA10为串口通讯的发送和接收引脚
PA15我用到了其他的功能(在本文中并没有用到,也懒得删除)
PB0-PB3是矩阵键盘的列输入(需要设置为输入,至于是下拉输入还是上拉输入,需要看你代码怎么写,我用的是下拉输入)
PB4-PB7是矩阵键盘的行输出(需要设置为推挽输出,因为我PB0-PB3设置的是下拉输入,所以输出的初始化应该设置为低电平)
PB13-PB15是LCD1602的功能端,分别为E端、RW端、RS端
PC0-PC7为LCD1602的数据输入端
以下为Proteus中的连接图。
本程序主要记录4*4矩阵键盘的逻辑和LCD1602的驱动方法,次要编写串口通信的程序。在矩阵键盘中,这里用的是按行扫描的方法,每次只返回一个值,不会产生重复的按键响应,并且每按下一次按键只会给串口发送一次数据,不会重复发送,松开按键后才会等待下一次按键响应;LCD1602的驱动方法可以去查看数据手册,这里只列出几个重要的配置数据,本程序中有显示单个字节,显示一行字符等;另外例如在严谨的程序中,防止程序跑飞,还应加入看门狗之类的,本文并没有列出,有需要的请自行在主函数中加入喂狗就可以。
lcd1602指令介绍:
指令1:清显示,指令码01H,光标复位到地址00H位置
指令2:光标复位,光标返回到地址00H
指令3:光标和显示位置设置I/D,光标移动方向,高电平右移,低电平左移,S:屏幕上所有文字是否左移或右移,高电平表示有效,低电平表示无效。
指令4:显示开关控制。D:控制整体的显示开与关,高电平表示开显示,低电平表示关显示。C:控制光标的开与关,高电平表示有光标,低电平表示无光标B:控制光标是否闪烁,高电平闪烁,低电平不闪烁。
指令5:光标或显示移位 S/C :高电平时显示移动的文字,低电平时移动光标
指令6:功能设置命令 DL:高电平时为4位总线,低电平时为8位总线N:低电平时为单行显示,高电平时为双行显示,F:低电平时显示5X7的点阵字符,高电平时显示5X10的显示字符。
指令7:字符发生器RAM地址设置。
指令8:DDRAM地址设置。
指令9:读忙信号和光标地址 BF:忙标志位,高电平表示忙,此时模块不能接收命令或数据,如果为低电平表示不忙。
本程序中的初始化函数中用到的有指令1、指令4、指令6、指令7。
下图为写指令和写数据所用图。
当写指令时,需要先将RS和RL全部置低电平,然后再写指令;当写数据时,需要先将RS置为高电平,将RW置为低电平,然后再写入数据;当使能端口E有上升沿时便会将执行指令或者执行数据。
lcd1602.h文件
- #ifndef __LCD1602_H
- #define __LCD1602_H
- #include "stm32f10x.h"
-
- //PC引脚定义为 lcd的数据输入端
- #define LCD_DATA GPIOC
-
- //lcd的RS端定义 PB15
- #define LCD_RS_EN GPIO_SetBits(GPIOB,GPIO_Pin_15)
- #define LCD_RS_CLEAR GPIO_ResetBits(GPIOB,GPIO_Pin_15)
-
- //lcd的RW端定义 PB14
- #define LCD_RW_EN GPIO_SetBits(GPIOB,GPIO_Pin_14)
- #define LCD_RW_CLEAR GPIO_ResetBits(GPIOB,GPIO_Pin_14)
-
- //lcd的E端定义 PB13
- #define LCD_E_EN GPIO_SetBits(GPIOB,GPIO_Pin_13)
- #define LCD_E_CLEAR GPIO_ResetBits(GPIOB,GPIO_Pin_13)
-
- //lcd输入数据
- #define DATAOUT(x) GPIO_Write(LCD_DATA,x)
-
- void LCD1602_Init(void);
- void LCD1602_gpio_Init(void);
- void LCD1602_CMD(u8 cmd);
- void LCD1602_DATA(u8 data);
- void LCD1602_Busy(void);
- void LCD1602_Clear(void);
- void LCD1602_Show_Str(u8 rol,u8 line,u8 *str);
- void LCD1602_Show_Bit(u8 rol,u8 line,u8 showdata);
-
- #endif
lcd1602.c文件
- #include "lcd1602.h"
- #include "delay.h"
-
- void LCD1602_Init(void)
- {
- LCD1602_gpio_Init(); //GPIO初始化
-
- delay_ms(15);
- LCD1602_CMD(0x38); //16*2显示 8位数据线 5*7点阵
- delay_ms(5);
- LCD1602_CMD(0x0c); //开显示 无光标
- LCD1602_CMD(0x06); //文字不动 地址自动+1
- LCD1602_CMD(0x01); //清屏
- }
-
- //写指令
- void LCD1602_CMD(u8 cmd)
- {
- LCD1602_Busy(); //检测忙不忙
- LCD_RS_CLEAR; //RS 0
- LCD_RW_CLEAR; //RW 0
- LCD_E_CLEAR;
- DATAOUT(cmd);
- delay_ms(1);
- LCD_E_EN;
- delay_ms(5);
- LCD_E_CLEAR;
- }
-
- //写数据
- void LCD1602_DATA(u8 data)
- {
- LCD1602_Busy(); //检测忙不忙
- LCD_RS_EN; //RS 1
- LCD_RW_CLEAR; //RW 0
- LCD_E_CLEAR;
- DATAOUT(data);
- delay_ms(1);
- LCD_E_EN;
- delay_ms(5);
- LCD_E_CLEAR;
- }
-
- //清屏
- void LCD1602_Clear(void)
- {
- LCD1602_CMD(0x01);
- }
-
- //字符串显示 rol 行 line 列
- void LCD1602_Show_Str(u8 rol,u8 line,u8 *str)
- {
- if(rol == 1)
- LCD1602_CMD(0x00+line+0x80);
- else if(rol == 2)
- LCD1602_CMD(0x40+line+0x80);
- while(*str != '\0')
- {
- LCD1602_DATA(*str);
- str++;
- }
- }
-
- //字符显示
- void LCD1602_Show_Bit(u8 rol,u8 line,u8 showdata)
- {
- if(rol == 1)
- LCD1602_CMD(0x00+line+0x80);
- else if(rol == 2)
- LCD1602_CMD(0x40+line+0x80);
- LCD1602_DATA(showdata);
- }
-
- //检测忙函数
- void LCD1602_Busy(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
- GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7; //PC7
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOC,&GPIO_InitStruct);
-
- LCD_RS_CLEAR; //RS 0
- LCD_RW_EN; //RW 1
-
- while(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_7));
-
- GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7; //PC7
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOC,&GPIO_InitStruct);
- }
-
- //gpio初始化
- void LCD1602_gpio_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOB,ENABLE);
-
- GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出
- GPIO_InitStruct.GPIO_Pin=0X00FF; //PC0-PC7
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOC,&GPIO_InitStruct);
- GPIO_ResetBits(GPIOC,0x00ff);
-
- GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //PB13-PB15
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOB,&GPIO_InitStruct);
- GPIO_ResetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
- }
本程序逻辑比较简单,采用的是行扫描方法,程序中有一点需要注意就是一定要搞清楚两个for循环的逻辑开始和结束,否则很容易导致虽然设置了按键按下标志位,但是还是会出现重复按键的问题,另外就是对gpio口的配置,到底是应该设置为输出还是输入,其他的emmmm我还没想到,这两点注意到应该就没问题了。虽然写的程序有点啰嗦,但是简单易懂,还有很多可以优化的方法,我就简单说一个简化方法,可以将列扫描函数在头文件里面直接define也是可以的,大家可以自行简化和优化。
key.h
- #ifndef __KEY_H
- #define __KEY_H
-
- #include "sys.h"
- #include "stm32f10x.h"
-
- //这里将按键重定义 方便后期修改IO口
-
- #define Key_Gpio GPIOB
-
- #define line1 GPIO_Pin_4
- #define line2 GPIO_Pin_5
- #define line3 GPIO_Pin_6
- #define line4 GPIO_Pin_7
-
- #define rol1 GPIO_Pin_0
- #define rol2 GPIO_Pin_1
- #define rol3 GPIO_Pin_2
- #define rol4 GPIO_Pin_3
-
-
- void Key_Init(void);
- u8 Key_Scan(void);
- void Key_gpio_Init(void);
- void Key_set(u8 key,u8 mode);
- u8 Rol_state(u8 rol);
-
-
- #endif
key.c
- #include "key.h"
- #include "stm32f10x.h"
- #include "delay.h"
- #include "uart.h"
-
- void Key_Init(void)
- {
- Key_gpio_Init();
- }
-
- u8 Key_Scan(void)
- {
- u8 i,j;
- u8 Key_Value = '.';
- static u8 Key_up_flag = 1; //不支持连按
-
- for(i=1;i<=4;i++) //一共是四行,i的值为行数
- {
- Key_set(i,1); //将改行置为高电平,其余行置为低电平,保证只有一行是高电平
-
- for(j=1;j<=4;j++) // 一共四列 四列分别检测 另一种方法可以不去单个检测,去做一个统一的判断
- {
- if(Rol_state(j) == 1)
- {
- delay_ms(5); //按键消抖
-
- if(Rol_state(j) == 1 && Key_up_flag == 1)
- {
- Key_up_flag = 0;
-
- switch(j)
- {
- case 1:Key_Value = (4*(i-1))+1;break;
- case 2:Key_Value = (4*(i-1))+2;break;
- case 3:Key_Value = (4*(i-1))+3;break;
- case 4:Key_Value = (4*(i-1))+4;break;
- default:break;
- }
-
- //USART_SendData(USART1,Key_Value); //此条注释可以验证是否是每次只触发一次按键响应,即不支持连按
- }
- }
- }
- }
-
- //在for循环结束之后 将行全部置为高电平 检测列是否有高电平 如果没有 即没有按键按下 此时就将行全部置为低电平 标志位置为1 等待下一次按键按下
- Key_set(5,1);
- if(Rol_state(1) == 0 && Rol_state(2) == 0 && Rol_state(3) == 0 && Rol_state(4) == 0 && Key_up_flag == 0)
- {
- Key_up_flag = 1;
- Key_Value = '.';
- Key_set(5,0);
- }
-
-
- //进行按键的重定义
- switch(Key_Value)
- {
- case 1:Key_Value = '1';break;
- case 2:Key_Value = '2';break;
- case 3:Key_Value = '3';break;
- case 4:Key_Value = 'A';break;
- case 5:Key_Value = '4';break;
- case 6:Key_Value = '5';break;
- case 7:Key_Value = '6';break;
- case 8:Key_Value = 'B';break;
- case 9:Key_Value = '7';break;
- case 10:Key_Value = '8';break;
- case 11:Key_Value = '9';break;
- case 12:Key_Value = 'C';break;
- case 13:Key_Value = '*';break;
- case 14:Key_Value = '0';break;
- case 15:Key_Value = '#';break;
- case 16:Key_Value = 'D';break;
- default:Key_Value = '.';break;
- }
-
- return Key_Value;
- }
-
-
- //gpio的初始化
- void Key_gpio_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
-
- GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD; //下拉输入
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOB,&GPIO_InitStruct);
-
- GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOB,&GPIO_InitStruct);
-
- GPIO_ResetBits(GPIOB,GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
- }
-
-
- //行的置电平函数
- void Key_set(u8 key,u8 mode) //key: 哪一行 mode: 高电平还是低电平
- {
- switch(key)
- {
- case 1:
- if(mode == 1)
- {
- GPIO_SetBits(Key_Gpio,line1);
- GPIO_ResetBits(Key_Gpio,line2);
- GPIO_ResetBits(Key_Gpio,line3);
- GPIO_ResetBits(Key_Gpio,line4);
- }
- else if(mode == 0)
- GPIO_ResetBits(Key_Gpio,line1);
- break;
- case 2:
- if(mode == 1)
- {
- GPIO_SetBits(Key_Gpio,line2);
- GPIO_ResetBits(Key_Gpio,line1);
- GPIO_ResetBits(Key_Gpio,line3);
- GPIO_ResetBits(Key_Gpio,line4);
- }
- else if(mode == 0)
- GPIO_ResetBits(Key_Gpio,line2);
- break;
- case 3:
- if(mode == 1)
- {
- GPIO_SetBits(Key_Gpio,line3);
- GPIO_ResetBits(Key_Gpio,line2);
- GPIO_ResetBits(Key_Gpio,line1);
- GPIO_ResetBits(Key_Gpio,line4);
- }
- else if(mode == 0)
- GPIO_ResetBits(Key_Gpio,line3);
- break;
- case 4:
- if(mode == 1)
- {
- GPIO_SetBits(Key_Gpio,line4);
- GPIO_ResetBits(Key_Gpio,line2);
- GPIO_ResetBits(Key_Gpio,line3);
- GPIO_ResetBits(Key_Gpio,line1);
- }
- else if(mode == 0)
- GPIO_ResetBits(Key_Gpio,line4);
- break;
- case 5:
- if(mode == 1)
- GPIO_SetBits(Key_Gpio,line4|line3|line2|line1);
- else if(mode == 0)
- GPIO_ResetBits(Key_Gpio,line4|line3|line2|line1);
- break;
- default:break;
- }
- }
-
- //列的检测函数
- u8 Rol_state(u8 rol)
- {
- u8 Rol_state;
- switch(rol)
- {
- case 1: rol = rol1;break;
- case 2: rol = rol2;break;
- case 3: rol = rol3;break;
- case 4: rol = rol4;break;
- }
- Rol_state = GPIO_ReadInputDataBit(Key_Gpio,rol);
- return Rol_state;
- }
由于本文重点不是在串口通信,不在过多具体讲解,需要注意的是在串口程序里面出现的PA15引脚和motor字眼,这是我做的电机验证程序,自行忽略和删除即可。
uart.h
- #ifndef __UART_H
- #define __UART_H
- #include "sys.h"
-
- void Uart_Init(u32 bound);
- void Uart_gpio_Init(void);
- void Uart_NVIC_Init(void);
- void USART1_IRQHandler(void);
-
- #endif
uart.c
- #include "uart.h"
- #include "stm32f10x.h"
- #include "motor.h"
-
- void Uart_Init(u32 bound)
- {
- USART_InitTypeDef USART_InitStruct;
-
- Uart_gpio_Init();
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
-
- USART_DeInit(USART1);
-
- USART_InitStruct.USART_BaudRate=bound;
- USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
- USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
- USART_InitStruct.USART_Parity=USART_Parity_No;
- USART_InitStruct.USART_StopBits=USART_StopBits_1;
- USART_InitStruct.USART_WordLength=USART_WordLength_8b;
- USART_Init(USART1,&USART_InitStruct);
-
- Uart_NVIC_Init();
-
- USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
-
- USART_Cmd(USART1,ENABLE);
- }
-
- void Uart_gpio_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
-
- GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOA,&GPIO_InitStruct);
-
- GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOA,&GPIO_InitStruct);
- }
-
- void Uart_NVIC_Init(void)
- {
- NVIC_InitTypeDef NVIC_InitStruct;
-
- NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;
- NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0;
- NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
- NVIC_Init(&NVIC_InitStruct);
- }
-
- void USART1_IRQHandler(void)
- {
- u8 res;
- if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
- {
- res = USART_ReceiveData(USART1);
- USART_SendData(USART1,res);
- while(USART_GetFlagStatus(USART1,USART_IT_TC));
- GPIO_SetBits(GPIOA,GPIO_Pin_15);
- USART_ClearITPendingBit(USART1,USART_IT_RXNE);
- }
- }
-
main.c
- #include "stm32f10x.h"
- #include "lcd1602.h"
- #include "delay.h"
- #include "motor.h"
- #include "uart.h"
- #include "key.h"
-
- int main(void)
- {
- u8 str[] = "I LOVE YOU";
- u8 Key_Val;
- static int Key_val_Flag = 1;
-
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
-
- delay_init();
- Uart_Init(9600);
- LCD1602_Init();
- Motor_Init();
- Key_Init();
-
- while(1)
- {
- Key_Val = Key_Scan();
- if(Key_Val != '.'&& Key_val_Flag == 1)
- {
- Key_val_Flag = 0;
- LCD1602_Show_Bit(2,7,Key_Val);
- USART_SendData(USART1,Key_Val);
- }
- else if(Key_Val == '.' && Key_val_Flag == 0)
- {
- Key_val_Flag = 1;
- }
- LCD1602_Show_Str(1,3,str);
- }
- }
在仿真上的话,因为采用了串口,所以就需要用到虚拟串口和串口助手,串口助手我直接用的是在微软商店的串口调试助手,虚拟串口采用的是虚拟串口助手VSPD。
Proteus也可能存在一些对stm32系列仿真的bug,我就把我遇到的bug列在这里,可能大家可以用的到。
1、芯片频率
建议设置为48MHz,这样就差不多可以和实际时间一样,不然仿真会很慢。
2、 LCD1602
把我圈中的值设大一点,可能是刷新频率,不然屏幕上显示不出来或者就是有问题。
3、COMPIM
这两个波特率设置为9600和57600,另外没有奇偶校验,8位数据,1个停止位,相应的串口助手设置为9600波特率,其他一样。
4、 VIRTUAL TERMIN
波特率设置成57600,8位数据,没有奇偶校验,1个停止位。
这样大差不差就可以成功运行了!!!
新手一枚,文章有些冗杂,但是还是希望本文能够对大家有所帮助,加油兄弟们,总有一天会头秃的!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。