STM32F103 实验按键输入与串口实验

2023-11-17

目录

本文,在上一章的基础上,将介绍如下内容

  • 按键输入
  • 串口实验

上一篇:STM32F103实验跑马灯与蜂鸣器 https://blog.csdn.net/qq_40318498/article/details/95940254



正文

要进入按键输入,我们必须先明确,按键连在哪一个GPIO口,通过开发手册,如下
在这里插入图片描述
因此,我们可以在蜂鸣器和跑马灯的基础上,完成该实验。




按键输入

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "beep.h"
#define KEY0  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//读取按键0
#define KEY1  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取按键1
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键3(WK_UP) 

//#define KEY0 PEin(4)  PE4
//#define KEY1 PEin(3)	PE3
//#define WK_UP PAin(0) PA0,作用跟上面一样,可替换

#define KEY0_PRES 	1	//KEY0按下
#define KEY1_PRES	2	//KEY1按下
#define WKUP_PRES   3	//KEY_UP按下(即WK_UP/KEY_UP)

 int main(void)
 {
 	vu8 key=0;	
	delay_init();	    	 //延时函数初始化  
	LED_Init();		  		
	BEEP_Init();         	
	KEY_Init();         	//初始化与按键连接的硬件接口
	LED0=0;					//先点亮红灯
	while(1)
	{
 		key=KEY_Scan(0);	//得到键值
	  if(key)
		{						   
			switch(key)
			{				 
				case WKUP_PRES:	//控制蜂鸣器
					BEEP=!BEEP;
					break; 
				case KEY1_PRES:	//控制LED1翻转
					LED1=!LED1;
					break;
				case KEY0_PRES:	//同时控制LED0,LED1翻转
					LED0=!LED0;
					LED1=!LED1;
					break;
			}
		}else delay_ms(10); 
	}	 
}
void KEY_Init(void) //IO³õʼ»¯
{ 
 	GPIO_InitTypeDef GPIO_InitStructure;
 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//ʹÄÜPORTA,PORTEʱÖÓ

	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_4|GPIO_Pin_3;//KEY0-KEY1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
 	GPIO_Init(GPIOE, &GPIO_InitStructure);//

	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入 
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

//0,没有按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY3按下 WK_UP
//响应优先级 KEY0>KEY1>KEY_UP!!
u8 KEY_Scan(u8 mode)
{	 
	static u8 key_up=1;//按键松开
	if(mode)key_up=1;  		  
	if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
	{
		delay_ms(10);消抖
		key_up=0;
		if(KEY0==0)return KEY0_PRES;	//1
		else if(KEY1==0)return KEY1_PRES;	//2
		else if(WK_UP==1)return WKUP_PRES;	//3
	}else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1; 	    
 	return 0;//无按键按下
}
void LED_Init(void)
{
	 GPIO_InitTypeDef  GPIO_InitStructure;
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 
		
	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 
	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		
	 GPIO_Init(GPIOB, &GPIO_InitStructure);					
	 GPIO_SetBits(GPIOB,GPIO_Pin_5);						 
	
	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;	    		
	 GPIO_Init(GPIOE, &GPIO_InitStructure);	  				
	 GPIO_SetBits(GPIOE,GPIO_Pin_5); 						 
}
void BEEP_Init(void)
{
 
	 GPIO_InitTypeDef  GPIO_InitStructure;
	 	
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 
	 
	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;				 
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 
	 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 
	 GPIO_Init(GPIOB, &GPIO_InitStructure);	 
	 
	 GPIO_ResetBits(GPIOB,GPIO_Pin_8);

}

以上,我们设置一个按键是上拉输入,一个按键是下拉输入,那有什么区别呢?

1、上拉输入:上拉就是把电位拉高,比如拉到Vcc。上拉就是将不确定的信号通过一个电阻嵌位在高电平!电阻同时起限流作用!强弱只是上拉电阻的阻值不同,没有什么严格区分。
2、下拉输入:就是把电压拉低,拉到GND。与上拉原理相似。
对于KEY0和KEY1,通过上拉电阻,把引脚的电平嵌位在高电平,当按键按下时,按引脚电平转变成低电平。而对于WK_UP,因为是高电平触发,因此我们把它拉低。




串口实验

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
 
 int main(void)
 {		
 	u16 t;  
	u16 len;	
	u16 times=0;
	delay_init();	    	 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
	//设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 //串口初始化为115200
 	LED_Init();			 //LED端口初始化
	KEY_Init();          //初始化与按键连接的硬件接口
 	while(1)
	{
		//接收状态
		//bit15	接收完成标记
		//bit14£¬	接收到oxod
		//bit13~0   接收到的有效字节数目
		//u16 USART_RX_STA=0;       //接收状态标记
		if(USART_RX_STA&0x8000)	//如果接受到数据...
		{					   
			len=USART_RX_STA&0x3fff;//得到数据长度
			printf("\r\n你发送的消息为:\r\n\r\n");
			for(t=0;t<len;t++)
			{
				USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据
				while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
			}
			printf("\r\n\r\n");//插入换行
			USART_RX_STA=0;	
		}else
		{
			times++;
			if(times%200==0)printf("请输入数据\n");  
			if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行
			delay_ms(10);   
		}
	}	 
 }
 void uart_init(u32 bound){
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟

	//USART1_TX   GPIOA.9
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

	//USART1_RX	  GPIOA.10初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器

	//USART 初始化设置

	USART_InitStructure.USART_BaudRate = bound;//串口波特率
	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(USART1, &USART_InitStructure); //初始化串口1
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
	USART_Cmd(USART1, ENABLE);                    //使能串口1 

}
void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;
	#if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
		OSIntEnter();    
	#endif
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
	{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据

		if((USART_RX_STA&0x8000)==0)//接收未完成
		{
			if(USART_RX_STA&0x4000)//接收到了0x0d
			{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
			}
		else //还没收到0X0D
		{	
			if(Res==0x0d)USART_RX_STA|=0x4000;
			else
			{
				USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
				USART_RX_STA++;
				if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
			}		 
		}
	}   		 
	} 
	#if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
		OSIntExit();  											 
	#endif
} 

运行结果如下:
在这里插入图片描述



关于串口

ALIENTEK精英STM32开发板所使用的STM32F103ZET6最多可提供5路串口,有分数波特率发生器,支持同步单线通信和半双工单线通讯,支持LIN,支持调制解调器操作,智能卡协议和IrDA SIR ENDEC规范,具有DMA等。

	void uart_init(u32 bound)函数是串口1初始化函数
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA,ENABLE);
	//使用USART1,GPIOA,该口具有复用功能

接下来,我们要初始化相应的GPIO端口为特定的状态,如下:
在这里插入图片描述
所以需要配置TX(PA9)设置为推挽复用输出模式,将RX(PA10)设置为浮空输入模式

 //USART1_TX   PA.9     
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 复用推挽输出     	
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出     
 GPIO_Init(GPIOA, &GPIO_InitStructure);         //USART1_RX   PA.10 浮空输入    
  
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;     
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入     
 GPIO_Init(GPIOA, &GPIO_InitStructure);   

紧接着,我们要进行usart1的中断初始化,设置抢占优先级值和子优先级的值:

//Usart1 NVIC 中断配置 配置     
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级 3  
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //子优先级 3  
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   //IRQ 通道使能  
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化 VIC 寄存器 

以上涉及到的中断知识,将在下一次实验讲

USART1_IRQHandler函数

这是串口1的中断响应函数,当串口1发生了相应的中断后,就会跳到该函数执行。中断相应函数的名字不能随便定义,需要遵循MDK定义的函数名,这在startup_stm32f10x_hd.s文件中可以找到。

	if (USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)
	//判断是否接收中断,如果是串口接收中断,则读取串口接收到的数据。
	RES = USART_ReceiveData(USART1);	//(USART1->DR);	读取接收的数据

既然读取到了数据,那么我们就需要对数据进行分析。
一个接收状态寄存器USART_RX_STA(此寄存器其实就是一个全局变量),定义如下:
在这里插入图片描述
当接收到从电脑发过来的数据,把接收到的数据保存在USART_RX_BUF中,同时在接收状态寄存器(USART_RX_STA)中计数接收到的有效数据个数,当收到回车(回车由两个字节组成:0x0D和0x0A)的第一个字节0X0D时,计数器不再增加,等待0x0A的到来,如果0x0A没有到来,则认为这次接收失败,重新开始下一次的接收。

数据的发送和接收

STM32的发送与接收是通过数据寄存器USART_DR来实现的,这是一个双寄存器,包含了TDR和RDR。当向该寄存器写数据的时候,串口就会自动发送。当收到该数据的时候,也是存在该寄存器内.

	void USART_SendData(USART_TypeDef* USARTx,uint16_t Data);
	//通过该函数向串口寄存器USART_dR写入一个数据
	uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
	//通过该函数读取串口接收到的数据

总结

通过以上,我学会了基本的按键输入与串口实验,下一次学习外部中断和独立看门狗的实验。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32F103 实验按键输入与串口实验 的相关文章

随机推荐

  • 基础算法:高精度除法

    高精度除法 题目条件 除数一定不为0 include
  • Numpy 索引 排序

    numpy argmax 和 numpy argmin numpy argmax 和 numpy argmin 函数分别沿给定轴返回最大和最小元素的索引 实例 numpy sort numpy sort 函数返回输入数组的排序副本 函数格式
  • Spring学习(三)--声明式事务&常用注解

    1 前言 这篇文章用来整理spring中的事务管理机制 声明式事务和常用的注解 一般的事务管理分为两种 编程式事务和声明式事务 spring提供声明式事务的支持 这样在我们操作数据库时更加方便 2 声明式事务 声明式事务是在AOP的基础上实
  • Ubuntu16.04+GTX1060mq(驱动版本430.64)安装CUDA10.0

    Ubuntu16 04 GTX1060mq 驱动版本430 64 安装CUDA10 0 CUDA下载 cuda最新版本下载地址 可在该页面进入历史版本下载 https developer nvidia com cuda downloads
  • 环境+代理配置+模块化

    1 模块化知识补充 1 1 模块化的基础 1 引入目录 默认引入目录中的index js文件 vuex modules a js b js index js 引入a js 引入 b js import store from vuex 是vu
  • Flutter(一)之Flutter的的简单入门分析

    前言 Flutter诞生于2018年 谷歌出品 应该是属于最新的移动跨平台开发框架了 从React Native自身框架的一些问题导致的用户量降低后 有很多小伙伴便转战Flutter战场 Flutter作为最新的跨移动平台开发框架 自然是汲
  • 使用Python,matplotlib绘制Nomogram列线图

    使用Python matplotlib绘制Nomogram列线图 1 效果图 2 源码 参考 这篇博客将介绍如何使用Python matplotlib绘制列线图 写这篇博客源于博友的提问 期望使用matplotlib绘制列线图如下 翻官网文
  • 浏览器访问.m3u8文件

    浏览器播放m3u8文件
  • PostgreSQL - tablefunc

    文章目录 创建扩展 tablefunc函数 1 normal rand 产生一个正态分布随机值 高斯分布 的集合 2 crosstab 行转列 1 3 crosstab 行转列 2 4 connectby 官方文档地址 创建扩展 creat
  • 关于 android 远程控制(pc 控制手机)

    因为手机的触屏有问题 需要通过pc来控制手机 试用 vnc viewer 没有成功 而且它是基于 wifi 的 然后不放弃 继续查找 终于发现 androidscreencast Desktop app to control an andr
  • Electron将Web页面打包成桌面应用实例

    上一篇文章 Electron vue脚手架改造vue项目 介绍了如何将Vue项目构建成桌面应用的方法 这篇文章将继续介绍Electron构建桌面项目 不仅仅局限于Vue项目 使用Vue项目做案例 只是目前做的Vue项目比较多 拿其中一个练手
  • 修改MySQL/MariaDB数据库root用户名和密码

    MySQL 5 7以下版本 更改密码 mysql u root p Enter password mysql gt use mysql 选择数据库 Database changed mysql gt update user set pass
  • Execl “此单元格中的数字为文本格式,或者其前面有撇号” 设成数字格式

    如何一次性批量将数字前面加上 撇 Excel批量设置数字转化成 以文本形式存储的数字 Excel批量为数值列加绿色小三角 正解 1 在数值前加个单撇号 2 选中列 进行分列操作 具体操作如下 选中该列 点击 菜单栏 中的 数据 分列 下一步
  • 谈谈修改寄存器默认值的几种方法和实现

    一 DFF的类型介绍 寄存器默认值 也叫复位值 是当reset或者set有效时寄存器输出的值 对于一个DFF来说 如下图 当reset为0时 Q输出0 当set为0时 Q输出为1 外部使用时保证reset与set不同时为0 图一 也就是说
  • 通俗易懂的Vue异步更新策略及 nextTick 原理

    最近在学习一些底层方面的知识 所以想做个系列尝试去聊聊这些比较复杂又很重要的知识点 学习就好比是座大山 只有自己去登山 才能看到不一样的风景 体会更加深刻 今天我们就来聊聊Vue中比较重要的异步更新策略及 nextTick 原理 在聊话题之
  • MySQL基础使用:入门指南

    MySQL是一种流行的关系型数据库管理系统 广泛应用于各种应用程序开发中 掌握MySQL的基础使用对于开发人员来说是非常重要的 在本博客中 我们将介绍MySQL的基本概念 常用命令和示例 帮助您入门MySQL数据库的使用 一 MySQL的基
  • 修复ie浏览器主页被360篡改

    一 环境 win10 二 问题 打开ie浏览器 发现主页被360篡改了 360我没有装过 怀疑是其他软件修改的 分析 浏览器主页被篡改 一般都是注册表被修改了 或者是启动程序里面有加载篡改主页 首先 看是否启动程序里面有加载篡改了主页 用右
  • Flutter容器组件之尺寸限制类容器(ConstrainedBox,SizedBox,UnconstrainedBox)

    尺寸限制类容器 尺寸限制类容器用于限制容器大小 Flutter中提供了多种这样的容器 如ConstrainedBox SizedBox UnconstrainedBox AspectRatio等 1 ConstrainedBox Const
  • 区块链Blockchain

    区块链Blockchain 区块链是分布式数据存储 点对点传输 共识机制 加密算法等计算机技术的新型应用模式 所谓共识机制是区块链系统中实现不同节点之间建立信任 获取权益的数学算法 狭义来讲 区块链是一种按照时间顺序将数据区块以顺序相连的方
  • STM32F103 实验按键输入与串口实验

    目录 本文 在上一章的基础上 将介绍如下内容 按键输入 串口实验 上一篇 STM32F103实验跑马灯与蜂鸣器 https blog csdn net qq 40318498 article details 95940254 正文 要进入按