[标准库]STM32F103R8T6 点灯以及按键扫描

news2024/11/17 9:53:13

刚开始学32的时候,选择了基于HAL库进行开发,原因是HAL比较容易上手,像点灯、输出PWM、按键输入这种操作都很快捷。但是到ADC+DMA这部分的时候发现,HAL库有一些地方我认为不是很合理和方便。比如DMA中断这部分,ST官方给出的资料里面明确写着HTIE和TCIE这两个中断允许位,用户可以根据自己的需求自行选择DMA搬运数据是搬一半中断一下还是搬完了才中断一次。但在HAL库中,初始化DMA中断,一旦调用HAL_DMA_Start_IT这个DMA中断使能函数,DMA的三个中断(传输过半、传输完成、传输错误)都被使能,虽然可以想要用哪个中断就去哪个回调函数写相应的内容,但是还是让人十分不爽呀。而且这个是CubeMx初始化一次就会把HAL_DMA_Start_IT的内容初始化一次,所以说就算自己到这个函数里面去把不想要的中断给关掉,下次CubeMX生成的时候又会覆盖掉。于是最后我选择放弃HAL库开发,回到标准库开发来。

我是跟着野火的教程一步步来的,写这篇博客单纯为了记录学习路径,方便日后自己看,内容比较简单和粗鄙,让各位见笑了。

第一步–建工程

野火给的库(应该也是官方的库),下载下来解压得到这些文件
在这里插入图片描述
里面有用的只有Libraries和Project这两个文件夹里面的内容,Libraries这个文件夹里面放着后面需要使用的标准库,Project这个文件夹里面放着ST官方写的例程,用到芯片的哪个功能如果不会用,打开参考官方的使用方法就可以了。

Libraries这个文件夹里面有
在这里插入图片描述
核心文件里面的
在这里插入图片描述
CMSIS/CM3 里面有一个CoreSupport文件夹,这个文件夹里面的内容好像也是没用的,ST官方后续好像没有用这个文件夹里面的文件了。主要还是上图这个路径里面的这几个文件,stm32f10x.h这个文件火哥说是最重要的,相当于51单片机里面的reg.51.h文件。下面system_stm32f10x.h应该也是用于初始化时候的,不深究其作用了。然后第一个startup文件夹里面有很多个以.s结尾的启动文件,因为stm32f10有很多个不同的型号,比如103c8t6、103R8T6、103ZET6,这些不同的型号其实底层是一样,但是一些内存容量有区别,所以启动文件应该也是有不同,反正就是根据自己使用的MCU容量大小来选择不同的启动文件。我这里用的是R8T6,对应是中等容量,选择_md后缀的启动文件.

开始新建工程目录(我这里保持跟教程一样)
在这里插入图片描述
分为三个文件夹:library(专门用来放库)、Project(专门用来放MDK的工程目录)、User(专门用来放我自己写的代码文件)

library里面内容如下:
在这里插入图片描述
就是把上面核心文件里面的东西拷出来了而已,core_cm3.h这个文件应该是没有用的,拷不拷都不影响,然后上面startup文件夹里面放的是所有的启动文件(其实用到的只有这个startup_stm32f10x_md.s)

Project就不说了,MDK新建一个工程的存放路径就在这个文件夹里。

User文件夹里面我放了
在这里插入图片描述
这个conf.h 其实就是为了代码简洁,不用在每个文件里面都包含一堆的.h头文件,只要是把conf.h这个头文件一包含,那么就包含了所有的外设类的头文件。算是个中间人一样的东西。

建立好文件夹之后就要去MDK里面建立工程,并且建立工程目录,然后把文件添加进来。
在这里插入图片描述
在Target 1右键Add Group添加四个文件夹进来,一个是CMSIS是放核心的.c文件,一个User是放用户写的代码,一个FWLib是放外设那一堆的.c文件,然后Startup文件夹是放启动文件。然后双击这些文件夹,把需要用到的库的所有.c文件选择进来,再把自己定义的main.c之类的用户代码.c文件也添加进来。添加了这些.c文件之后,还要在MDK里面设置这些文件的路径。
在这里插入图片描述
这里选的路径是选.h文件的路径,而且要选择到放.h文件的文件夹的那一层路径(就是MDK进入这个文件夹可以看到.h文件),如果选到.h文件的上一层,MDK都会找不到.h文件。

添加完路径之后,这个地方写这两句话
在这里插入图片描述
意思是MDK在帮我们编译的时候,后台帮我们编译这两句话,第一句是因为要使用conf.h文件才写的,打开stm32f10x.h文件拉到最下面可以看到:
在这里插入图片描述
意思是 如果有 #define USE_STDPERIPH_DRIVER 这一句话,就包含conf.h文件进来,包含了这个中间文件就代表我们包含了所有的外设设备的.h文件进来。

写完USE_STDPERIPH_DRIVER 这句之后 接一个英文的逗号 然后写STM32F10X_MD 意思应该是选择启动文件 反正也是库里面的宏在起作用,如果是大容量,那么这个地方MD应该是要换成别的。我就不细究了。

做完上面这些步骤之后,配置一下仿真器,然后就可以开始写代码了,这个基础环境以后还能复制来用。

第二部–点灯

我在User文件下下新建了一个文件夹(bsp_led:板级支持包_led) 里面放的是bsp_led.c和bsp_led.h,用于存放函数原型和声明文件。

bsp_led.c内容如下:
只有一个初始化led那个IO的初始化函数

#include "bsp_led.h"

void led_gpio_init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);			//开启相应GPIO口的时钟
	//LED接在PA8口上,所以要开GPIOA的时钟,查手册可以查到GPIOA是挂载在APB2时钟总线上的
    GPIO_InitTypeDef GPIO_InitStruct;								//声明一个结构体,类似int A;

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;							//给结构体里面的变量赋初值
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;					//这些初值应该填什么,可以去查GPIO_InitStruct这个结构体里面有哪些变量,ST官方有写这些变量可以填什么值
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;					//设置PIN8为推挽输出并设置IO口速度为50Mhz

    GPIO_Init(GPIOA,&GPIO_InitStruct);								//调用初始化GPIO的函数,第一个形参写GPIO(A-G) 选择自己使用的,第二个形参就是把上面声明的结构体的地址取过来。
    																//这个函数就初始化了上面我们设置的Pin的工作模式
}

在main.c中:

#include "stm32f10x.h"
#include "bsp_led.h"


void Delay(__IO uint32_t nCount)	 //简单的延时函数
{
	for(; nCount != 0; nCount--);
}

int main(void)
{
	led_gpio_init();
	
	while(1)
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_8);				//ST官方给的库函数,将某位置一
		Delay(50000);
		GPIO_ResetBits(GPIOA,GPIO_Pin_8);			//ST官方给的库函数,将某位清零
		Delay(50000);	
	}
}

main函数很简单,首先在进入while循环之前把灯的IO初始化一下,初始化完了之后就可以控制IO的输出状态了,在while循环里面我是让灯闪烁,不过这里的delay延时太短了,肉眼看不出闪烁效果,用示波器是可以看到IO的是一个方波信号。

做到这里点灯就已经成功了。下一步把按键扫描加进来,在User文件夹内新建了一个bsp_key的文件夹,里面存放了bsp_key.c和bsp_key.h两个文件。

按键扫描

bsp_key.c内容如下:

#include "bsp_key.h"



void key_gpio_init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
    GPIO_InitTypeDef GPIO_InitStruct;

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;


    GPIO_Init(GPIOA,&GPIO_InitStruct);			//初始化1按键  PA15


    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;


    GPIO_Init(GPIOA,&GPIO_InitStruct);			//初始化2按键  PA0
}


uint8_t key_scan(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
    if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON)			//KEY_ON被宏定义为了0
    {
		Delay(100000);
		if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON)
		{
			//松手检测
			while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);//如果一直按下,则一直在这里等待
			return KEY_ON;										//松手之后就返回一个按键按下的结果
		}
		else return KEY_OFF;									//如果消抖之后判断按键没有按下了,那么还是返回按键没按下的值(KEY_OFF被宏定义为1)
    }
    else return KEY_OFF;
}


void Delay(__IO uint32_t nCount)	 //简单的延时函数		注意这里写了之后,main函数的那个要删掉
{
	for(; nCount != 0; nCount--);
}

bsp.h的内容如下:

#ifndef __BSP_KEY_H				//如果编译时没有包含过bsp.h文件  则执行下面的包含语句  如果已经被包含过了  那么就不再执行这个头文件里面的语句了.
#define __BSP_KEY_H
/*
这个地方多说两句: 这种书写头文件的方法是用于这样的场合  比如main.c里面要包含一下bsp_key.h这个文件 
Key_scan.c这个文件里面又包含bsp_key.h这个文件,那么在编译的时候,如果不加#ifndef __BSP_KEY_H
和#endif这种条件编译的语句的话,在编译器编译main.c第一次遇到#include "bsp.h"的时候,
会进入这个头文件把里面的函数和变量声明一次,在后面编译Key_scan.c的时候又遇到#include "bsp.h",
编译器又会把头文件里面的函数和声明又声明一次,就会导致重定义的错误。如果加了条件编译的语句,
编译器第二次进来这个头文件会判断一下这个头文件有没有被包含过,如果有,那么就不再执行下面的声明语句了
*/


#include "stm32f10x.h"


#define  KEY_ON  0
#define  KEY_OFF 1


void key_gpio_init(void);				//按键的GPIO口初始化
void Delay(__IO u32 nCount); 			//这里声明一下Delay函数,方便给主函数也调用

uint8_t key_scan(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);		//按键扫描函数


#endif

主函数现在是这样的:

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_key.h"

unsigned int i=0;

#define GPIOA_ODR_Addr	(GPIOA_BASE+12)
#define PAout(n) *(unsigned int*)((GPIOA_ODR_Addr & 0xF0000000)+0x02000000+((GPIOA_ODR_Addr & 0x00FFFFFF)<<5)+(n<<2))


int main(void)
{
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
	led_gpio_init();
	key_gpio_init();
	
	while(1)
	{
		if(key_scan(GPIOA,GPIO_Pin_15) == KEY_ON)
		{
			PAout(8) = 1;
		}
		
		if(key_scan(GPIOA,GPIO_Pin_0) == KEY_ON)
		{
			PAout(8) = 0;
		}	
	}
}

#define PAout(n)
这句代码是定义位带操作,解释下什么意思:
STM32不能像51单片机那样通过sbit单独定义一个IO,那么如果我想频繁的改变同一组IO下的不同pin,如果使用ST官方给的函数,我就得操作ODR寄存器,比如我要操作GPIOA PIN0为1,GPIOA PIN1为0,那我首先要操作ODR寄存器的第一位&0,然后再操作ODR寄存器第0位|1,这样比较麻烦,尤其是如果需要连续操作同一组IO下的很多个pin。
那么在STM32上,官方为了这种位操作,把SRAM和外设区的低1MB空间内的每一个bit都膨胀成了一个32位的字,拿ODR寄存器来说明一下,意思就是 ODR寄存器里面有32个bit,其中高16位是没用的,低16位中,每一位控制这对应的IO输出状态,比如我要GPIOA PIN0输出1,那我就往ODR寄存器的第0位
写1。位带别名区的意思就是 把ODR寄存器的第0位映射到另一片的内存里,并且这一个bit对应的别名区的地址是32bit的。
那么不难理解,这1MB空间映射到别名区,别名区的大小就是扩大32倍了,所以别名区就是32MB这么大。

野火教程里面有这样一张图
在这里插入图片描述
图片比较直观。片上SRAM一般应该很少需要用到位带别名区,因为我们可以定义bit变量。但是外设区的位带别名区还是比较实用的。

计算当前的寄存器的某一位对应的是位带别名区的哪一片地址,是由公式计算出来的,这个公式我没有深究,以后复制来用可以了。

这个程序运行起来就是,按下1按键,led熄灭,按下2按键,led点亮。比较简单。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/130172.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

音视频开发系列--H264编解码总结

一、概述 H264&#xff0c;通常也被称之为H264/AVC&#xff08;或者H.264/MPEG-4 AVC或MPEG-4/H.264 AVC&#xff09; 对摄像头采集的每一帧视频需要进行编码&#xff0c;由于视频中存在空间和时间的冗余&#xff0c;需要用算法来去除这些冗余。H264是专门去除这些冗余的算法…

王者荣耀崩溃解决记录

王者荣耀竟然崩溃了 上周玩王者荣耀&#xff0c;突然就进不去了&#xff0c;点击开始游戏后应用直接就崩溃退出了。 第一反应&#xff0c;肯定是反馈给游戏客服。但是果然腾讯的游戏是找不到真客服的&#xff0c;全部都是机器人处理的&#xff0c;给了我一个毫无用处的官方回…

springboot中配置文件优先级以及分类,这你都可以不会吗?不会赶紧进来学( ̄(∞) ̄)

各位小伙伴大家好呀┗( ▔, ▔ )┛&#xff0c;马上过年了&#xff0c;但是感觉没啥期待的哈哈哈哈哈&#xff0c;现在的年说实话真的挺没劲的呜呜。 言归正传&#xff0c;我们大家在使用springboot时难免会写各种各样的配置信息&#xff0c;比如port&#xff0c;jdbc啊这些&am…

2022这一年:阳了、变轨和逆风

又到年末了&#xff0c;2022这一年应该会让人记忆深刻&#xff0c;于我而言这一年的感受有明显的分界线&#xff0c;在此之前的世界温暖一些&#xff0c;提供着能量&#xff0c;让人心生探索它的纷繁多彩&#xff1b;今年世界变得寒冷了&#xff0c;展示着它的严酷与无情。阳了…

再学C语言20:循环控制语句——for循环

在while循环中&#xff0c;建立一个重复执行固定次数的循环涉及到3个动作&#xff1a; 1&#xff09;初始化一个计数器 2&#xff09;计数器与某个有限的值比较 3&#xff09;每次执行循环&#xff0c;要在循环体中让计数器的值递增 其中&#xff0c;计数器的初始化在循环之…

【pandas】教程:6-如何计算摘要统计

Pandas 计算摘要统计 本节使用的数据为 data/titanic.csv&#xff0c;链接为 pandas案例和教程所使用的数据-机器学习文档类资源-CSDN文库 加载数据 import pandas as pdtitanic pd.read_csv("data/titanic.csv") titanic.head()PassengerId Survived Pclass \…

#Z0424. 树上的旅行

题目 Description 给出一棵有N个结点的树&#xff0c;给出Q个询问&#xff0c;求结点xj过结点K到节点yj的最短距离 Format Input 第一行一个数n 接下来共有n-1行&#xff0c;三个数u,v,len表示u和v之间存在一条边长为len 再给你Q&#xff0c;K。代表有Q个询问&#xff0…

视频 | bedtools使用介绍1

点击阅读原文跳转完整教案。基因组中的趣事&#xff08;二&#xff09;- 最长的基因2.7 million&#xff0c;最短的基因只有8 nt却能编码基因组中的趣事&#xff08;一&#xff09;&#xff1a;这个基因编码98种转录本1 Linux初探&#xff0c;打开新世界的大门1.1 Linux系统简介…

10000+条数据的内容滚动功能如何实现?

遇到脑子有问题的产品经理该怎么办&#xff1f;如果有这么一个需求要你在一个可视区范围内不间断循环滚动几千上万条数据你会怎么去实现&#xff1f; 且不说提这个需求的人是不是脑子有问题&#xff0c;这个需求能不能实现&#xff1f;肯定是可以的&#xff0c;把数据请求回来渲…

2022蓝桥杯省赛C++A组初尝试

前言 耗时三个半小时&#xff0c;看看自己不懂的有多少&#xff0c;以便明确后续备赛2023方向 耗时3个半小时&#xff0c;只拿了18分&#xff0c;没学过&#xff0c;时间再多也做不出来&#xff0c;有奥数那感觉了 据说蓝桥杯省3得做对 2填空 2大题&#xff08;30分&#x…

PMP®项目管理|不同场景使用不同沟通方式

不同沟通方式的确有适用场景和不适用场景。无效沟通的重要原因之一就是错误选择沟通方式。 我们会在工作中用到很多沟通方式&#xff0c;每种沟通方式都有适用的场合&#xff0c;也有不适用的场合&#xff0c;错误选择将使沟通变得低效甚至无效。 沟通方式主要有三种&#xf…

一百种语言的LOVE

2023年快要到来啦&#xff0c;很高兴这次我们又能一起度过~ 目录 一、前言 二、详细介绍 三、效果展示 四、代码编写 index.html script.js style.css 五、获取代码 需要源码&#xff0c;可以私信我(⊙o⊙)&#xff1f;关注我&#xff1f; 一、前言 时光荏苒&#xf…

vue element-ui 手机号校验 验证码校验 获取验证码倒数60秒无样式实现

这段时间被迫搞前端搞裂开了&#xff0c;记录一下手机号验证码校验登录的极简无样式前端实现 巨丑&#xff01;希望大佬们不介意 下面是先演示效果 点击登陆后显示校验信息 输入手机号点击获取验证码 输入符合校验的内容后点击登录提示成功 无后端交互&#xff01;&#…

从档案信息管理到档案知识管理

今年6月份的时候&#xff0c;笔者发过一篇文章《DIKW模型在档案信息资源开发中的应用》&#xff0c;简要阐述了知识管理领域非常著名的DIKW模型&#xff0c;即从数据&#xff08;Data&#xff09;→信息&#xff08;Information&#xff09;→知识&#xff08;Knowledge&#x…

基于SpringBoot和微信小程序的餐馆点餐系统的设计和实现

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…

Android 学习笔记

目录一.Android入门1.Android 概述2.Android Studio3.创建模拟器4.使用外部模拟器5.第一个app二.app开发基础1.开发语言2.app工程目录结构3.文本控件TextView(1)设置文本内容(2)设置文本大小(3)设置文本颜色(4)设置背景颜色(5)设置视图宽高(6)设置视图间距(7)设置视图对齐方式4…

fpga实操训练(硬件乘法器)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 fpga上面的资源一般分成lut、pin、dff、dsp、pll。至于rom、ram、fifo&#xff0c;一般也是基于dff和lut来创建的&#xff0c;之前我们也讨论过。今…

ABAP 之ALV展示及下钻弹窗数据页面

序 HELLO, 这里是百里一个学习中的ABAPER,这里记录工作学习中遇到的bug,知识内容等内容.今天讲的是在工作中经常会使用的数据下钻,通过跳出小界面的方式展示关联数据.今天结合工作经验这里做下技术总结. 下钻简介 在ALV报表展示中.会出现关键字段下钻,展示某个界面或者系统自…

C语言基础--操作符详解

文章目录一、操作符1. 算数操作符2. 移位操作符&#xff08;1&#xff09;右移操作符举例补充&#xff08;2&#xff09;左移操作符举例分析&#xff08;3&#xff09;警告3.位操作符&#xff08;1&#xff09;按位与&#xff08;2&#xff09;按位或&#xff08;3&#xff09;…

小程序之后台交互--个人中心

目录一、微信登录流程简介二、微信用户信息获取1、index.js2、index.wxml三、微信登录流程代码详解1、后台准备①导入微信小程序SDK②application.yml③WxProperties④WxConfig⑤WxAuthController1、登录-小程序①login.js②user.js③util.js四、emoji的存储1、修改配置文件my.…