stm32学习(启动+LED+按键)

stm32学习心得

提示:写博客纯属第一次练手,不足之处希望多提建议

前言


首先,小编拙见,拿到一样新的东西,先去了解什么原理啥的都是扯淡,因为在很多时候我们都更喜欢关注我们看到的,用眼睛去发现,所以我觉得拿到一样没见过的,我们可以先拿到一个成熟的demo跑一下对吧,但是工欲善其事必先利其器,在小编用过的裸机编程软件中,有keil跟cube,cube目前是一个很多新人学习的软件但是小编用了很不喜欢(网上说有一些bug,小编没发现,只是用不习惯而已),而且对于底层他好像屏蔽了(有些省略)所以第一步便是弄软件


一、keil的安装

小编不多说,这种程度的东西我找一个给您
link
这里补充几句里面的东西没有的话评论区留言,小编会更新到百度云,但是网上很多

二、跑demo之前,先了解一下stm32是啥先铺垫

. . . 在小编看来,不管是stm32还是其他的微控器像aruino跟51,都可以总结为是一些根据编译后的二进制文件运行相对应功能的可编程芯片,或者说一直可以DIY的微控器,可以通过语言(c/c++/python/lua)的编写,去控制io引脚达到数据传输或者控制外部器件以及设置内部寄存器达到一些定时啊或者dma功能,而要先去了解一些东西,像led灯,GPIO是啥,万用表示波器那些仪器的使用(看现象的同时通过数字的形式去感知),元器件跟mcu之间要共地,他的电压在多少既可以启动又不会导致烧坏(重要),led怎么亮的,5年前对于大一的我完全,,,,,所以避免有年级小一点的懵B,而这些都是小编这两年指导一些学生过程中出现最多的问题,当然都是拙见,更权威的回答应该是基于某种架构公司啊什么半导体啊,但是不关我事 ,也不管你事,刚入门只有能用就行
提示:后面还是得回去看到底是啥

三、运行一个demo看现象


最简单的就是流水灯了吧(视频手机里好像看不到),所以借鉴下原子哥的工程(对于开源,我服原子哥)

led_

四、根据现象提问题

1.是怎么点灯的

很明显,led就是发光二极管对吧,他是分正负极的,假如led共地,那么我们就是给他一个高电平亮:

库:

GPIO_SetBits(GPIOx,GPIO_Pin_x); 

寄存器:

GPIOB_ODR &= ~(1<<0); // 灭灯1 
GPIOB_ODR |= (1<<0);  // 点灯1

补充:GPIOB_ODR ——>#define GPIOB_ODR (*(unsigned int *)0x40010C0C) 地址映射

2.他点灯之前干了什么(上电开始)

STM32系列单片机有三种启动模式

(1)从Flash启动,将主Flash地址0x08000000映射到0x00000000,这样代码启动之后就相当于从0x08000000开始。

(2)从RAM启动,将RAM地址0x20000000映射到0x00000000,这样代码启动之后就相当于从0x20000000开始。

(3)从系统存储器启动。首先控制BOOT0 BOOT1管脚,复位后,STM32与上述两种方式类似,从系统存储器地址0x1FFF F000开始执行代码。系统存储器存储的其实就是STM32自带的bootloader代码,在bootloader中提供了UART1的接口,通过此接口可以将用户所需的程序代码下载到主Flash中,下载完毕后,此时程序代码已经存储在主Flash当中,这时切换启动模式(从主Flash启动),复位后所执行的就是刚刚下载到Flash中的代码了。
boot启动

(1)用户闪存 : 芯片内置的Flash。正常的工作模式。
(2)SRAM: 芯片内置的RAM区,就是内存。可以用于调试。
(3)系统存储器: 芯片内部一块特定的区域,芯片出厂时在这个区域预置了一段Bootloader,就是通常说的ISP程序。这个区域的内容在芯片出厂后没有人能够修改或擦除,即它是一个ROM区。启动的程序功能由厂家设置。

Main Flash memory:
是STM32内置的Flash,一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。

System memory:

从系统存储器启动,这种模式启动的程序功能是由厂家设置的。一般来说,这种启动方式用的比较少。
系统存储器是芯片内部一块特定的区域,STM32在出厂时,由ST在这个区域内部预置了一段BootLoader,也就是我们常说的ISP程序,这是一块ROM,出厂后无法修改。

一般来说,我们选用这种启动模式时,是为了从串口下载程序,因为在厂家提供的BootLoader中,提供了串口下载程序的固件,可以通过这个BootLoader将程序下载到系统的Flash中。但是这个下载方式需要以下步骤:

Step1:将BOOT0设置为1,BOOT1设置为0,然后按下复位键,这样才能从系统存储器启动BootLoader;

Step2:最后在BootLoader的帮助下,通过串口下载程序到Flash中;

Step3:程序下载完成后,又有需要将BOOT0设置为GND,手动复位,这样,STM32才可以从Flash中启动,可以看到,利用串口下载程序还是比较的麻烦,需要跳帽跳来跳去的,非常的不注重用户体验。

Embedded Memory:

内置SRAM,既然是SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试。

假如我只修改了代码中一个小小的地方,然后就需要重新擦除整个Flash,比较的费时,可以考虑从这个模式启动代码(也就是STM32的内存中),用于快速的程序调试,等程序调试完成后,在将程序下载到SRAM中。

思路来源链接: link

剩下的启动流程链接: link

五.问题

其他的引脚都可以拿来点灯?有可能拿来其他用?
当然有,像串口跟usb或者输入输出
而STM32 的 IO 口可以由软件配置成如下 8 种模式:
1、输入浮空
2、输入上拉
3、输入下拉
4、模拟输入
5、开漏输出
6、推挽输出
7、推挽式复用功能
8、开漏复用功能
具体怎么用,后续讲解

六 回顾详解_led

我们可以发现在我们打开一个工程的时候,系统给我们提供了启动方式跟初始化一些硬件啥的,方便我们在这个基础上开发,而接下来就是怎么通过这个基础进行开发了
而从最简单的点灯开始,我们学习c语言最简单的是printf(“hello world”);而点灯就是c的hello word

寄存器

void LED_Init(void)
{
	RCC->APB2ENR|=1<<3;    //使能PORTB时钟	   	 
	RCC->APB2ENR|=1<<6;    //使能PORTE时钟	
	   	 
	GPIOB->CRL&=0XFF0FFFFF; 
	GPIOB->CRL|=0X00300000;//PB.5 推挽输出   	 
  GPIOB->ODR|=1<<5;      //PB.5 输出高
											  
	
	GPIOE->CRL&=0XFF0FFFFF;
	GPIOE->CRL|=0X00300000;//PE.5推挽输出
	GPIOE->ODR|=1<<5;      //PE.5输出高 
}

这是一个简单的io初始化引脚的过程,这里初始化的GPIOB5跟GPIOE5,首先,我们初始化GPIOE的时钟,然后我们可以看看rcc的结构体,

typedef struct
{
  __IO uint32_t CR;
  __IO uint32_t CFGR;
  __IO uint32_t CIR;
  __IO uint32_t APB2RSTR;
  __IO uint32_t APB1RSTR;
  __IO uint32_t AHBENR;
  __IO uint32_t APB2ENR;
  __IO uint32_t APB1ENR;
  __IO uint32_t BDCR;
  __IO uint32_t CSR;

#ifdef STM32F10X_CL  
  __IO uint32_t AHBRSTR;
  __IO uint32_t CFGR2;
#endif /* STM32F10X_CL */ 

#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)   
  uint32_t RESERVED0;
  __IO uint32_t CFGR2;
#endif /* STM32F10X_LD_VL || STM32F10X_MD_VL || STM32F10X_HD_VL */ 
} RCC_TypeDef;

今天被一兄弟问道,RCC时钟拿来干嘛的,首先时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令。时钟系统就是CPU的脉搏,决定cpu速率,像人的心跳一样 只有有了心跳,人才能做其他的事情,而单片机有了时钟,才能够运行执行指令,才能够做其他的处理 (点灯,串口,ADC), 使用任何外设都需要时钟才能启动,
在这里插入图片描述
在这里插入图片描述

而gpioBE是挂在在APB2这条线上的,继而我们参考他的寄存器手册可以发现,B引脚在第三位,所以左移了三位,而E引脚在第六位,所以左移了六位,这里是用于初始化EB类的io,而我们上面说到,一个io他有模式,不同模式对应不同功能,对于我们这次点亮led并不能说我脑子里让他输出他就输出对吧,
所以我们看看他的接下来的配置,

	GPIOB->CRL&=0XFF0FFFFF; 
	GPIOB->CRL|=0X00300000;//PB.5 推挽输出   

我么通过看手册可以知道STM32 的每个 IO 端口都有 7 个寄存器来控制:
1.配置模式的 2 个 32 位的端口配置寄存器 CRL 和 CRH;2 个 32 位的数据寄存器 IDR 和 ODR;
2.1 个 32 位的置位/复位寄存器BSRR;
3.一个 16 位的复位寄存器 BRR;
4.1 个 32 位的锁存寄存器 LCKR;
我们常用的 IO 端口寄存器只有 4 个:CRL、CRH、IDR、ODR。
CRL 和 CRH 控制着每个 IO 口的模式及输出速率,那他们两个有啥不同呢
CRL是对一类io低八位的设置,ex:PE0~PE7
CRH是对一类io高八位的设置,exc:PE0~PE7

在这里插入图片描述
我们说到这张图是对低八位io的设置,而CRL 是一个32位的寄存器,所以一个io对于4位,所以,0-3对应的是GPIOx.1引脚,而0-1对应的是MODEy[1:0]的设置,而2-3对应的是CNFy[1:0]的设置,这里是GPIOB.5在20-23,所以我们先把他清零,然后我们设置为推挽输出(00),输出速率为50M(11),所以为3,20-23位或上3,就设置了PB5为输出引脚,然后提供ODR使其输出1或0就行

GPIOB->CRL&=0XFF0FFFFF; 
GPIOB->CRL|=0X00300000;//PB.5 推挽输出 
GPIOB->ODR|=1<<5;      //PB.5 输出高

库函数

库函数就简单了,通过 RCC_APB2PeriphClockCmd使能时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 //使能PB,PE端口时钟

通过把设置写入GPIO_InitTypeDef结构体就可以了

typedef struct
{
  uint16_t GPIO_Pin;             /*!< Specifies the GPIO pins to be configured.
                                      This parameter can be any value of @ref GPIO_pins_define */

  GPIOSpeed_TypeDef GPIO_Speed;  /*!< Specifies the speed for the selected pins.
                                      This parameter can be a value of @ref GPIOSpeed_TypeDef */

  GPIOMode_TypeDef GPIO_Mode;    /*!< Specifies the operating mode for the selected pins.
                                      This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;
void LED_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 //使能PB,PE端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //LED0-->PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.5
 GPIO_SetBits(GPIOB,GPIO_Pin_5);						 //PB.5 输出高

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;	    		 //LED1-->PE.5 端口配置, 推挽输出
 GPIO_Init(GPIOE, &GPIO_InitStructure);	  				 //推挽输出 ,IO口速度为50MHz
 GPIO_SetBits(GPIOE,GPIO_Pin_5); 						 //PE.5 输出高 
}

六 回顾详解_按键

stm32中按键输入的配置

这里的RCC->APB2ENR可以参考上面的讲解

//按键初始化函数
void KEY_Init(void)
{
	RCC->APB2ENR|=1<<2;     //使能PORTA时钟
	RCC->APB2ENR|=1<<6;     //使能PORTE时钟
	GPIOA->CRL&=0XFFFFFFF0;	//PA0设置成输入,默认下拉	  
	GPIOA->CRL|=0X00000008; 
	  
	GPIOE->CRL&=0XFFF000FF;	//PE2~4设置成输入	  
	GPIOE->CRL|=0X00088800; 				   
	GPIOE->ODR|=7<<2;	   	//PE2~4 上拉
} 

我们也依旧可着这张图看看,我们初始化完时钟之后对于按键我们就得初始化io为输入,对于pa0,我们首先清空io配置,然后设置CNFy为10,对于MODEy我么设置00,1000那就是8,即可
sda
在这里插入图片描述

总结

提示:这里对文章进行总结:

此次想写一个单片机从上电到led的流程,还有一些细节性的缺漏,比如pc指针跟启动文件那些的,望各位读者大佬多多建议

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的示例代码,演示了如何使用按键控制LED启动和停止: ```c #include "stm32f10x.h" int main(void) { // 初始化GPIO GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; // 按键连接到PA0和PA1 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入 GPIO_Init(GPIOA, &GPIO_InitStructure); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; // LED连接到PB8 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); // 无限循环 while (1) { if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == RESET) // 如果按键1被按下 { GPIO_SetBits(GPIOB, GPIO_Pin_8); // 打开LED while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == RESET); // 等待按键1释放 } if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == RESET) // 如果按键2被按下 { GPIO_ResetBits(GPIOB, GPIO_Pin_8); // 关闭LED while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == RESET); // 等待按键2释放 } } } ``` 在这个例子中,我们使用PA0和PA1作为输入引脚来连接两个按键,PB8作为输出引脚来连接LED。在无限循环中,我们使用GPIO_ReadInputDataBit函数来检测按键是否被按下,并使用GPIO_SetBits和GPIO_ResetBits函数来控制LED的开关。同时,为了避免按键抖动的影响,我们在按键被按下后等待按键被释放。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值