当前位置:   article > 正文

STM32F103系列 Proteus仿真 4*4矩阵键盘 串口+LCD1602显示_proteus矩阵键盘仿真电路

proteus矩阵键盘仿真电路

        最近一直在肝STM32系列芯片,这里我想要检验一下自己的初步学习成果,简单的运用一下IO口的配置,代码使用正点的库函数模板写的,仿真软件是Proteus 8.13,代码编写编译软件是Keil MDK5,仿真芯片是STM32F103R6小容量芯片。


目录

一、硬件设计

 二、软件设计

1、LCD1602的编写       

2、4*4 矩阵键盘的编写

 3、串口通信的编写

 4、主函数

三、仿真

 四、结尾


一、硬件设计

        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的驱动方法可以去查看数据手册,这里只列出几个重要的配置数据,本程序中有显示单个字节,显示一行字符等;另外例如在严谨的程序中,防止程序跑飞,还应加入看门狗之类的,本文并没有列出,有需要的请自行在主函数中加入喂狗就可以。

1、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文件

  1. #ifndef __LCD1602_H
  2. #define __LCD1602_H
  3. #include "stm32f10x.h"
  4. //PC引脚定义为 lcd的数据输入端
  5. #define LCD_DATA GPIOC
  6. //lcd的RS端定义 PB15
  7. #define LCD_RS_EN GPIO_SetBits(GPIOB,GPIO_Pin_15)
  8. #define LCD_RS_CLEAR GPIO_ResetBits(GPIOB,GPIO_Pin_15)
  9. //lcd的RW端定义 PB14
  10. #define LCD_RW_EN GPIO_SetBits(GPIOB,GPIO_Pin_14)
  11. #define LCD_RW_CLEAR GPIO_ResetBits(GPIOB,GPIO_Pin_14)
  12. //lcd的E端定义 PB13
  13. #define LCD_E_EN GPIO_SetBits(GPIOB,GPIO_Pin_13)
  14. #define LCD_E_CLEAR GPIO_ResetBits(GPIOB,GPIO_Pin_13)
  15. //lcd输入数据
  16. #define DATAOUT(x) GPIO_Write(LCD_DATA,x)
  17. void LCD1602_Init(void);
  18. void LCD1602_gpio_Init(void);
  19. void LCD1602_CMD(u8 cmd);
  20. void LCD1602_DATA(u8 data);
  21. void LCD1602_Busy(void);
  22. void LCD1602_Clear(void);
  23. void LCD1602_Show_Str(u8 rol,u8 line,u8 *str);
  24. void LCD1602_Show_Bit(u8 rol,u8 line,u8 showdata);
  25. #endif

 lcd1602.c文件

  1. #include "lcd1602.h"
  2. #include "delay.h"
  3. void LCD1602_Init(void)
  4. {
  5. LCD1602_gpio_Init(); //GPIO初始化
  6. delay_ms(15);
  7. LCD1602_CMD(0x38); //16*2显示 8位数据线 5*7点阵
  8. delay_ms(5);
  9. LCD1602_CMD(0x0c); //开显示 无光标
  10. LCD1602_CMD(0x06); //文字不动 地址自动+1
  11. LCD1602_CMD(0x01); //清屏
  12. }
  13. //写指令
  14. void LCD1602_CMD(u8 cmd)
  15. {
  16. LCD1602_Busy(); //检测忙不忙
  17. LCD_RS_CLEAR; //RS 0
  18. LCD_RW_CLEAR; //RW 0
  19. LCD_E_CLEAR;
  20. DATAOUT(cmd);
  21. delay_ms(1);
  22. LCD_E_EN;
  23. delay_ms(5);
  24. LCD_E_CLEAR;
  25. }
  26. //写数据
  27. void LCD1602_DATA(u8 data)
  28. {
  29. LCD1602_Busy(); //检测忙不忙
  30. LCD_RS_EN; //RS 1
  31. LCD_RW_CLEAR; //RW 0
  32. LCD_E_CLEAR;
  33. DATAOUT(data);
  34. delay_ms(1);
  35. LCD_E_EN;
  36. delay_ms(5);
  37. LCD_E_CLEAR;
  38. }
  39. //清屏
  40. void LCD1602_Clear(void)
  41. {
  42. LCD1602_CMD(0x01);
  43. }
  44. //字符串显示 rol 行 line 列
  45. void LCD1602_Show_Str(u8 rol,u8 line,u8 *str)
  46. {
  47. if(rol == 1)
  48. LCD1602_CMD(0x00+line+0x80);
  49. else if(rol == 2)
  50. LCD1602_CMD(0x40+line+0x80);
  51. while(*str != '\0')
  52. {
  53. LCD1602_DATA(*str);
  54. str++;
  55. }
  56. }
  57. //字符显示
  58. void LCD1602_Show_Bit(u8 rol,u8 line,u8 showdata)
  59. {
  60. if(rol == 1)
  61. LCD1602_CMD(0x00+line+0x80);
  62. else if(rol == 2)
  63. LCD1602_CMD(0x40+line+0x80);
  64. LCD1602_DATA(showdata);
  65. }
  66. //检测忙函数
  67. void LCD1602_Busy(void)
  68. {
  69. GPIO_InitTypeDef GPIO_InitStruct;
  70. GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入
  71. GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7; //PC7
  72. GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  73. GPIO_Init(GPIOC,&GPIO_InitStruct);
  74. LCD_RS_CLEAR; //RS 0
  75. LCD_RW_EN; //RW 1
  76. while(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_7));
  77. GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出
  78. GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7; //PC7
  79. GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  80. GPIO_Init(GPIOC,&GPIO_InitStruct);
  81. }
  82. //gpio初始化
  83. void LCD1602_gpio_Init(void)
  84. {
  85. GPIO_InitTypeDef GPIO_InitStruct;
  86. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOB,ENABLE);
  87. GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出
  88. GPIO_InitStruct.GPIO_Pin=0X00FF; //PC0-PC7
  89. GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  90. GPIO_Init(GPIOC,&GPIO_InitStruct);
  91. GPIO_ResetBits(GPIOC,0x00ff);
  92. GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出
  93. GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //PB13-PB15
  94. GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  95. GPIO_Init(GPIOB,&GPIO_InitStruct);
  96. GPIO_ResetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
  97. }

2、4*4 矩阵键盘的编写

        本程序逻辑比较简单,采用的是行扫描方法,程序中有一点需要注意就是一定要搞清楚两个for循环的逻辑开始和结束,否则很容易导致虽然设置了按键按下标志位,但是还是会出现重复按键的问题,另外就是对gpio口的配置,到底是应该设置为输出还是输入,其他的emmmm我还没想到,这两点注意到应该就没问题了。虽然写的程序有点啰嗦,但是简单易懂,还有很多可以优化的方法,我就简单说一个简化方法,可以将列扫描函数在头文件里面直接define也是可以的,大家可以自行简化和优化。

 key.h

  1. #ifndef __KEY_H
  2. #define __KEY_H
  3. #include "sys.h"
  4. #include "stm32f10x.h"
  5. //这里将按键重定义 方便后期修改IO口
  6. #define Key_Gpio GPIOB
  7. #define line1 GPIO_Pin_4
  8. #define line2 GPIO_Pin_5
  9. #define line3 GPIO_Pin_6
  10. #define line4 GPIO_Pin_7
  11. #define rol1 GPIO_Pin_0
  12. #define rol2 GPIO_Pin_1
  13. #define rol3 GPIO_Pin_2
  14. #define rol4 GPIO_Pin_3
  15. void Key_Init(void);
  16. u8 Key_Scan(void);
  17. void Key_gpio_Init(void);
  18. void Key_set(u8 key,u8 mode);
  19. u8 Rol_state(u8 rol);
  20. #endif

key.c

  1. #include "key.h"
  2. #include "stm32f10x.h"
  3. #include "delay.h"
  4. #include "uart.h"
  5. void Key_Init(void)
  6. {
  7. Key_gpio_Init();
  8. }
  9. u8 Key_Scan(void)
  10. {
  11. u8 i,j;
  12. u8 Key_Value = '.';
  13. static u8 Key_up_flag = 1; //不支持连按
  14. for(i=1;i<=4;i++) //一共是四行,i的值为行数
  15. {
  16. Key_set(i,1); //将改行置为高电平,其余行置为低电平,保证只有一行是高电平
  17. for(j=1;j<=4;j++) // 一共四列 四列分别检测 另一种方法可以不去单个检测,去做一个统一的判断
  18. {
  19. if(Rol_state(j) == 1)
  20. {
  21. delay_ms(5); //按键消抖
  22. if(Rol_state(j) == 1 && Key_up_flag == 1)
  23. {
  24. Key_up_flag = 0;
  25. switch(j)
  26. {
  27. case 1:Key_Value = (4*(i-1))+1;break;
  28. case 2:Key_Value = (4*(i-1))+2;break;
  29. case 3:Key_Value = (4*(i-1))+3;break;
  30. case 4:Key_Value = (4*(i-1))+4;break;
  31. default:break;
  32. }
  33. //USART_SendData(USART1,Key_Value); //此条注释可以验证是否是每次只触发一次按键响应,即不支持连按
  34. }
  35. }
  36. }
  37. }
  38. //在for循环结束之后 将行全部置为高电平 检测列是否有高电平 如果没有 即没有按键按下 此时就将行全部置为低电平 标志位置为1 等待下一次按键按下
  39. Key_set(5,1);
  40. if(Rol_state(1) == 0 && Rol_state(2) == 0 && Rol_state(3) == 0 && Rol_state(4) == 0 && Key_up_flag == 0)
  41. {
  42. Key_up_flag = 1;
  43. Key_Value = '.';
  44. Key_set(5,0);
  45. }
  46. //进行按键的重定义
  47. switch(Key_Value)
  48. {
  49. case 1:Key_Value = '1';break;
  50. case 2:Key_Value = '2';break;
  51. case 3:Key_Value = '3';break;
  52. case 4:Key_Value = 'A';break;
  53. case 5:Key_Value = '4';break;
  54. case 6:Key_Value = '5';break;
  55. case 7:Key_Value = '6';break;
  56. case 8:Key_Value = 'B';break;
  57. case 9:Key_Value = '7';break;
  58. case 10:Key_Value = '8';break;
  59. case 11:Key_Value = '9';break;
  60. case 12:Key_Value = 'C';break;
  61. case 13:Key_Value = '*';break;
  62. case 14:Key_Value = '0';break;
  63. case 15:Key_Value = '#';break;
  64. case 16:Key_Value = 'D';break;
  65. default:Key_Value = '.';break;
  66. }
  67. return Key_Value;
  68. }
  69. //gpio的初始化
  70. void Key_gpio_Init(void)
  71. {
  72. GPIO_InitTypeDef GPIO_InitStruct;
  73. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
  74. GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD; //下拉输入
  75. GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
  76. GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  77. GPIO_Init(GPIOB,&GPIO_InitStruct);
  78. GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出
  79. GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
  80. GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  81. GPIO_Init(GPIOB,&GPIO_InitStruct);
  82. GPIO_ResetBits(GPIOB,GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
  83. }
  84. //行的置电平函数
  85. void Key_set(u8 key,u8 mode) //key: 哪一行 mode: 高电平还是低电平
  86. {
  87. switch(key)
  88. {
  89. case 1:
  90. if(mode == 1)
  91. {
  92. GPIO_SetBits(Key_Gpio,line1);
  93. GPIO_ResetBits(Key_Gpio,line2);
  94. GPIO_ResetBits(Key_Gpio,line3);
  95. GPIO_ResetBits(Key_Gpio,line4);
  96. }
  97. else if(mode == 0)
  98. GPIO_ResetBits(Key_Gpio,line1);
  99. break;
  100. case 2:
  101. if(mode == 1)
  102. {
  103. GPIO_SetBits(Key_Gpio,line2);
  104. GPIO_ResetBits(Key_Gpio,line1);
  105. GPIO_ResetBits(Key_Gpio,line3);
  106. GPIO_ResetBits(Key_Gpio,line4);
  107. }
  108. else if(mode == 0)
  109. GPIO_ResetBits(Key_Gpio,line2);
  110. break;
  111. case 3:
  112. if(mode == 1)
  113. {
  114. GPIO_SetBits(Key_Gpio,line3);
  115. GPIO_ResetBits(Key_Gpio,line2);
  116. GPIO_ResetBits(Key_Gpio,line1);
  117. GPIO_ResetBits(Key_Gpio,line4);
  118. }
  119. else if(mode == 0)
  120. GPIO_ResetBits(Key_Gpio,line3);
  121. break;
  122. case 4:
  123. if(mode == 1)
  124. {
  125. GPIO_SetBits(Key_Gpio,line4);
  126. GPIO_ResetBits(Key_Gpio,line2);
  127. GPIO_ResetBits(Key_Gpio,line3);
  128. GPIO_ResetBits(Key_Gpio,line1);
  129. }
  130. else if(mode == 0)
  131. GPIO_ResetBits(Key_Gpio,line4);
  132. break;
  133. case 5:
  134. if(mode == 1)
  135. GPIO_SetBits(Key_Gpio,line4|line3|line2|line1);
  136. else if(mode == 0)
  137. GPIO_ResetBits(Key_Gpio,line4|line3|line2|line1);
  138. break;
  139. default:break;
  140. }
  141. }
  142. //列的检测函数
  143. u8 Rol_state(u8 rol)
  144. {
  145. u8 Rol_state;
  146. switch(rol)
  147. {
  148. case 1: rol = rol1;break;
  149. case 2: rol = rol2;break;
  150. case 3: rol = rol3;break;
  151. case 4: rol = rol4;break;
  152. }
  153. Rol_state = GPIO_ReadInputDataBit(Key_Gpio,rol);
  154. return Rol_state;
  155. }

 3、串口通信的编写

        由于本文重点不是在串口通信,不在过多具体讲解,需要注意的是在串口程序里面出现的PA15引脚和motor字眼,这是我做的电机验证程序,自行忽略和删除即可。

uart.h

  1. #ifndef __UART_H
  2. #define __UART_H
  3. #include "sys.h"
  4. void Uart_Init(u32 bound);
  5. void Uart_gpio_Init(void);
  6. void Uart_NVIC_Init(void);
  7. void USART1_IRQHandler(void);
  8. #endif

 uart.c

  1. #include "uart.h"
  2. #include "stm32f10x.h"
  3. #include "motor.h"
  4. void Uart_Init(u32 bound)
  5. {
  6. USART_InitTypeDef USART_InitStruct;
  7. Uart_gpio_Init();
  8. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
  9. USART_DeInit(USART1);
  10. USART_InitStruct.USART_BaudRate=bound;
  11. USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
  12. USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
  13. USART_InitStruct.USART_Parity=USART_Parity_No;
  14. USART_InitStruct.USART_StopBits=USART_StopBits_1;
  15. USART_InitStruct.USART_WordLength=USART_WordLength_8b;
  16. USART_Init(USART1,&USART_InitStruct);
  17. Uart_NVIC_Init();
  18. USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
  19. USART_Cmd(USART1,ENABLE);
  20. }
  21. void Uart_gpio_Init(void)
  22. {
  23. GPIO_InitTypeDef GPIO_InitStruct;
  24. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  25. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
  26. GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
  27. GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
  28. GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  29. GPIO_Init(GPIOA,&GPIO_InitStruct);
  30. GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
  31. GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
  32. GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
  33. GPIO_Init(GPIOA,&GPIO_InitStruct);
  34. }
  35. void Uart_NVIC_Init(void)
  36. {
  37. NVIC_InitTypeDef NVIC_InitStruct;
  38. NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;
  39. NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
  40. NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0;
  41. NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
  42. NVIC_Init(&NVIC_InitStruct);
  43. }
  44. void USART1_IRQHandler(void)
  45. {
  46. u8 res;
  47. if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
  48. {
  49. res = USART_ReceiveData(USART1);
  50. USART_SendData(USART1,res);
  51. while(USART_GetFlagStatus(USART1,USART_IT_TC));
  52. GPIO_SetBits(GPIOA,GPIO_Pin_15);
  53. USART_ClearITPendingBit(USART1,USART_IT_RXNE);
  54. }
  55. }

 4、主函数

main.c

  1. #include "stm32f10x.h"
  2. #include "lcd1602.h"
  3. #include "delay.h"
  4. #include "motor.h"
  5. #include "uart.h"
  6. #include "key.h"
  7. int main(void)
  8. {
  9. u8 str[] = "I LOVE YOU";
  10. u8 Key_Val;
  11. static int Key_val_Flag = 1;
  12. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  13. delay_init();
  14. Uart_Init(9600);
  15. LCD1602_Init();
  16. Motor_Init();
  17. Key_Init();
  18. while(1)
  19. {
  20. Key_Val = Key_Scan();
  21. if(Key_Val != '.'&& Key_val_Flag == 1)
  22. {
  23. Key_val_Flag = 0;
  24. LCD1602_Show_Bit(2,7,Key_Val);
  25. USART_SendData(USART1,Key_Val);
  26. }
  27. else if(Key_Val == '.' && Key_val_Flag == 0)
  28. {
  29. Key_val_Flag = 1;
  30. }
  31. LCD1602_Show_Str(1,3,str);
  32. }
  33. }

三、仿真

         在仿真上的话,因为采用了串口,所以就需要用到虚拟串口和串口助手,串口助手我直接用的是在微软商店的串口调试助手,虚拟串口采用的是虚拟串口助手VSPD。

        Proteus也可能存在一些对stm32系列仿真的bug,我就把我遇到的bug列在这里,可能大家可以用的到。

        1、芯片频率

        建议设置为48MHz,这样就差不多可以和实际时间一样,不然仿真会很慢。

        2、 LCD1602

        把我圈中的值设大一点,可能是刷新频率,不然屏幕上显示不出来或者就是有问题。

         3、COMPIM

        这两个波特率设置为9600和57600,另外没有奇偶校验,8位数据,1个停止位,相应的串口助手设置为9600波特率,其他一样。

        4、 VIRTUAL TERMIN

        波特率设置成57600,8位数据,没有奇偶校验,1个停止位。

         这样大差不差就可以成功运行了!!!

 四、结尾

        新手一枚,文章有些冗杂,但是还是希望本文能够对大家有所帮助,加油兄弟们,总有一天会头秃的!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/200631
推荐阅读
相关标签
  

闽ICP备14008679号