第一天与单片机基础知识:V1_stm32(寄存器版本)-龙虾哥(B站:奇遇单片机)

单片机基础知识

1.单片机中如何通过结构体(指针)来读写寄存器

知识:

GPIO寄存器地址就是偏移+首地址        关键:它是连续的        (每移位一次4字节)

结构体的成员地址也是连续的          定义int型(uint32_t,地址无符号)正好对上了每次移位4字节

让结构体首地址对上第一个寄存器地址,这样就可以通过结构体操作寄存器

注意:存储器映像(地址对应表),可以查看寄存器地址

考点:GPIO7个寄存器

因为:知道有多少个寄存器,定义结构体的时候就知道应该定义多少个成员

代码:

结构体对应上GPIO寄存器,

typedef struct
{
	uint32_t CRL;
	uint32_t CRH;
	uint32_t IDR;
	uint32_t ODR;
	uint32_t BSRR;
	uint32_t BRR;
	uint32_t LCKR;
}My_struct;

void test(void)
{
	int a;
	a = (*((My_struct*)(0x40010800))).BRR;
}

或者宏定义更方便

常规定义:

#define  GPIO (*((My_struct*)(0x40010800)))

void test(void)
{
	int a;
	a = GPIO.BRR;
}

定义结构体指针,指针(地址)指向

#define  GPIO (((My_struct*)(0x40010800)))

void test(void)
{
	int a;
	a = GPIO->BRR;
}

2.C语言位域在单片机中的使用

代码

.h文件:

单片机16个引脚

#ifndef __GPIO_H
#define __GPIO_H

#include "stm32f10x.h"
//位域

typedef struct
{
	volatile unsigned short bit0:1;
	volatile unsigned short bit1:1;
	volatile unsigned short bit2:1;
	volatile unsigned short bit3:1;
	volatile unsigned short bit4:1;
	volatile unsigned short bit5:1;
	volatile unsigned short bit6:1;
	volatile unsigned short bit7:1;
	volatile unsigned short bit8:1;
	volatile unsigned short bit9:1;
	volatile unsigned short bit10:1;
	volatile unsigned short bit11:1;
	volatile unsigned short bit12:1;
	volatile unsigned short bit14:1;
	volatile unsigned short bit15:1; 
	
}GOIO_Bit_Typedef;

#define PORTB_Out	((GOIO_Bit_Typedef*)(&GPIOB->ODR))
#define PORTB_In	((GOIO_Bit_Typedef*)(&GPIOB->IDR))

#define PBout(n)	(PORTB_Out->bit##n)//PortB_Out->bitn
#define PBin(n)		(PORTB_In->bit##n)

#endif

Led.c文件

#include "GPIO.h"
void Led_Init(void)
{
	PBout(13) = 1;
	PBout(13) = 0;
}

3.什么是电平

高低电平

0V-0.7V:0         2.2V-3.3V:1

低电平                高电平

CPU 输出

124D=0111 1100B

上升沿,下降沿

脉冲

两个低电平中间的高电平就是脉冲(上升沿+高电平+下降沿)010

4.单片机与寄存器

1.主要内容

寄存器(任务书)

①.一般指的是:单片机它是指单片机外设相关的寄存器

②CPU内部的叫做工作寄存器R0,R1.....PC指针,堆栈指针

③RAM,外设(单片机内部模块),是处于CPU外部的硬件电路功能模块

单片机的引脚,工作模式两种:输入模式,输出模式

④二进制,每一位2种状态

⑤左移。&,|

懂寄存器:

1:首先搞清楚它的配置,二进制位配置,每一位都有其特定含义,了解其含义(主要根据宏定义学)。

2:每一位是具体是什么功能,到他前面看他的描述

为什么要学寄存器:

1.不是每个单片机都有库,不是每个人都有秘书帮你干活(公司、老板、总经理、总监),好多事情呢需要你自己去写一些八位单片机,还有一些16位一些低端的单片机。

无标准库和库函数,你只能通过寄存器来写。

2.老板说我们就需要一些便宜的单片机,有时候选择用什么单片机,不是你能选的

成本原因,老板说我们就需要一些便宜的单片机。

3.老工程师,就是用寄存器写,你要优化维护人家的代码,总不能写C语言吧,你没那本事也不敢。

老工程师的寄存器程序维护问题。

4.有些地方有寄存器比用库方便,懂寄存器肯定能懂库,库是寄存器的封装。

方便

难点:

其实就是首先你要对单片机它内部模块的工作原理你要懂,还有就是寄存器它后边的备注,就是它每一位表示什么意思

2.CPU与寄存器的关系:

1.

将CPU比喻为皇帝,寄存器比喻为太监,寄存器外设比喻为大臣

RAM:针对当庭讨论事件的临时文件,主要用来方便沟通用退朝后就丢掉了。(草稿?)
ROM:朝代制度,祖先、习俗规定等不可更改的。

2.

CPU是厨师,寄存器是服务员,外设是菜单
菜单有啥厨师才能做啥;服务员负责点菜传菜,两者之间沟通;

5、如何看STM32单片机手册?

需要啥?就看啥。

1、数据手册--硬件工程师        小、中、大容量三种


2、用户手册--软件工程师        统一小中大,只有一种

1.程序是如何下载的

2.电源是究竟是怎么一回事配合数据手册一起看

3.复位(上电、死机)

4.时钟信号

我们单片机就是一个数字电路,编程时:

首先第一步你要配置单片机的系统时钟,就像电脑一样你得配起来他的主频,让它的时钟能够正常运行,然后你才能让其他电路进行正常的工作。它是一个时序的电路。

5.RCC寄存器描述

这一部分我们可以看到有时钟控制,配置、中断,外设复位、使能。

使能:指始时钟使能,单片机为了控制功耗,它的时钟是可以开启和关闭的,当你没有开启相关电路的时钟的时候,这部分电路不工作的,功耗就低。

这些寄存器是干嘛用的?

6.单片机引脚、定时器、ADC、TIM、iic、spi,等等

  1. 介绍
  2. 特点,它擅长干嘛
  3. 功能,它具体能干嘛,如何干(重点看:功能框图
  4. 特点介绍(总体描述、内部结构可以知道有哪些功能)
  5. 寄存器

总结:用户手册,重点看功能框图,熟悉功能描述,稍微看一下寄存器,重点看状态寄存器SR

例如:你要单片机定时10s

1.单片机它是如何就是定时?=》使用定时器模块

2.定时器模块它为什么能定时?=》从用户手册发现,内部有一个叫做计数器的,它其实就是用来计时(计时时钟信号)的

3.单片机它是如何计数?=》从用户手册发现,如何计数

4.计算完10s,我又怎么知道?=》闹钟会响,手册发现它内部是有一个中断的(或者说标志位)

然后就是看这些东西怎么配置,完成你想要完成的功能。

从问题出发,单片机如何解决这个问题。大问题是多个小问题组成,由大到小,由小到大!

第一天

第1-1节:单片机入门与学习方法讨论

一、什么是嵌入式


1、单片微型计算机简称单片机。微型控制器MCU(微型控制单元)


2、嵌入式系统是软硬件相结合的计算机系统,软硬件可裁剪。


3、常规性的定义:以应用为中心,以计算机技术为基础,软硬件可裁剪,满足对功能,可靠性,成本,体积,功能等严格要求的专用计算机系统。

二、嵌入式与单片机


硬件: 单片机MCU、MPU、DSP、FPGA、CPLD

软件: 前后台系统(裸机程序) 、FreeRTOS、UCOS-II/III、Linux


Nor Flash: 存放程序的储存器,断电不丢失数据

SRAM: 内存,存放数据的储存器,断电丢失数据


数据手册: 硬件工程师需要看的

用户手册: 软件工程师需要看的

三、学单片机需要的基础知识


一、数字电路
1、数字电路相关的概念,名词。
2、组合逻辑电路,时序逻辑电路。
3、电平的概念,二进制,十六进制,十进制转换。
4、或门,与门,非门。常见的数字芯片74HC系列

二、模拟电路
1、二极管、三极管开关电、MOS开关电路
2、运放放大电路 (同相,反相,差分放大电路),电压比较器                                             

3、电源电路 (3.3V,5V,12V电源稳压电路)、反馈电路

三、C语言
1、数据与类型        2、运算符        3、if switch while for        4、数组         5、函数        6、指针7、结构和联合        8、宏

 四、学习单片机首先需要知道的事情


1、单片机正常工作需要的基本硬件电路是什么?
2、使用什么软件写程序? 编程语言是什么?
3、如何把程序下载进单片机里面?
4、点亮一个LED,掌握单片机程序的项目工程

五、寄存器、标准库、HAL库


1、其实没什么区别,都需要学习。严格来说,库不需要学,真正需要学的是单片机内部结构

2、C与算法 寄存器与单片机

3、单片机学习方法--实践出真知

学习初期:动手敲代码,别C+V别人的代码。

一遍看用户手册+看视频,一边自己写代码来验证自已理解的对不对

寄存器其实就是单片机的配置表(任务书),单片机如何工作,就根据寄存器里面的内容来进行工作的。

寄存器就是一个任务书,把单片机需要做的事情写进任务书(寄存器),然后单片机根据任务书(寄存器)去完成任务。

这就是编写程序的一个过程,我们需要把单片机要做的事情写进去这个过程其实就叫做初始化过程。

实践;反馈;理论;

在实践中丰富你的理论,纠正你的理论,你的理论指导你实践

第1-2节:STM32 单片机基本介绍

STM32F103C8T6

用户手册:

数据手册:

关键参数C8T

        48,64K,LQFR

1、STM32容量划分

不同容量都有自己的数据手册

但是用户手册呢它们是共用的

外设资源(中等容量数据手册)

2、STM32时钟系统

如何看?

简单总结:1;抓住一个关键(系统时钟SYSCLK) 2.看输入 3.看输出


关键:
抓住一个关键词叫系统时钟SYSCLK(最中心),跟我们电脑里面的主频是一个概念72MHZ,

输入:

系统时钟来自开关SW一共有三个:

HSI内部时钟(内部的RC振荡器产生了8M时钟)、HSE外部晶振产生的时钟(最大4-16M)、PLLCLK锁相环时钟。

内部和外部的时钟太低,达不到72M,一般不用,我们一般用锁相环时钟(PLLCLK)。锁相环时钟前面有个倍频器(放大),乘“x”多少就放大多少倍。一般使用外部晶振产生的8M(内部的不稳定比如在比较冷的地方),然后开关PLLSRC选外部晶振,PLLMUL再选“x9”,8x9=72Mhz。然后在这边我们选择锁相环,中间这一路。

输出:

AHB预分频器就是分频的(除号“/”降低频率),72不需要降,直接预分频选择一就是72÷1,出来之后是72。

通向:核心存储器和DMA;

通向:Cortex系统时钟:(你可以选择72MHZ八分频(9MHZ)或者不分)

        1.系统定时器来产生一个中断,用来控制操作系统时间片的切换

        2.写一个延迟函数

通向:CP的内核72MHZ

通向:两个总线时钟APB1,APB2

APB1,只能选择除以二(36MHZ),PCLK1是给APB1外设时钟:就是我们有一个外设,他的时钟源都是来自这里,所以我们在使用这些外设的时候,要开启它的时钟,这边有个外设时钟使能。

注意:所以STM32单片机,它有一个特点,你使用内部模块,你首先要把它对应的时钟要打开。一个与门,都为1才有效(这个时钟信号才是有效的)。

再往下APB1走,之前选两分频(分频系数不为1)要乘以二,也就是36×2=72,给到TIMXCLK定时器2-7,都是使用72兆的。

APB2同上分析。

APB2前走到ADC,ADC它其实是一个模数转换器,也需要时钟。这个时钟你可以选择(除以“/”,2、4、6、8),72÷6能达到12。除非前面输入56才能56÷4=14。

通向:最后AHB走到最后一根线,是SDIO的一个接口时钟

注意:低速内部时钟信号,使用的是32.768K,这个时钟一般是用来作为日历(内部RTC产生日历的)

然后,主时钟输出,单片机可以把它内部的时钟输出去(选一种);A单片机的MCO连到B单片机的OSC_IN(OSC_OUT接地)

只需要一个晶振可以给两个单片机,提供时钟信号

3、STM32内部模块图

4、STM32封装(外形)图

LQFP48:封装LQFP,引脚48

5、STM32引脚功能

FT:引脚可以容忍5v电压

第2-1节:STM32单片机最小系统

图1:

单片机、供电、晶振、复位、启动配置、下载端口

图2:

1引脚,备用电源电路

CR1220纽扣电池,工作电压3V,备用电源电路。

正常工作电源VCC3.3V,如果检测到输入电压为3.3V表示正常工作,输入为3V表示电源断掉。此时单片机会断开除了时间无关的电路,只保证时间正常。

3,4引脚,晶振电路:

两无极电容22pf+晶振32.768KHZ

5,6引脚,时钟电路

正常工作的时钟电路,晶振8MHZ+1M电阻+两个22pf无极电容

注:如果对时间要求不严格,可以使用单片机内部的RC震荡电路产生时钟信号

7引脚,复位电路

10K电阻+104无极电容+按键

按下按键电容短路,然后电容上端电压缓慢为0(接地),放手后又缓慢升上3.3V以此完成复位。

48脚单片机,四组电源(红色部分)

VDD1        ;VDD2        ;VDD3        ;

A(模拟)

要求不严格的情况下,可以将VDDA与三个VDD相连接都3.3V,并且将VSSA和三个VSS相连接都接地

以VDD2为例:

104无极电容主要作用电源滤波(电源有干扰),焊接电路电容尽量离两个引脚近一点,提高滤波效果

降压电路

USB供电,通过降压芯片(AMS1117-3.3)将usb的5v降到3.3v供电

保险丝,电流过大的保护

28脚,验证工作指示灯

下载程序1

引脚(三根线)

下载与调试

下载器插座

        D4保护,防止接反

一般ST-LINK接SWCLK/SWDIO

时钟线:        数据线:

地线GND

下载程序2(串口下载电路)

2个引脚,输入/输出(RX/TX)

再加一个电源和地,4根线

把我们单片机的TTL电平转换成USB信号.

需要CH340G芯片(串口转USB芯片)进行电平转换

接单片机

接USB

启动配置电路

一般直接BOOT1置0

BOOT0:0正常启动,1串口下载模式

第2-2节:STM32程序下载方式介绍

第一天第2-2节:STM32程序下载方式介绍_哔哩哔哩_bilibili

第3-1节:寄存器_标准库项目工程创建

参考:

第一天第3-1节:寄存器_标准库项目工程创建_哔哩哔哩_bilibili

寄存器和标准库

        点击       

选中即可

第4-1节:STM32 GPIO输入输出端口

主要内容:

  • 什么是GPIO?
  • 什么是电平?
  • STM32 GPIO简介
  • GPIO基本结构
  • 介绍GPIO的八种工作模式
  • GPIO的寄存器介绍
  • GPIO配置步骤

1、什么是GPIO?

GPIO(General Purpose Input Output)通用输入输出口,简称IO端口,也称单片机引脚。可以输入输出数据。

2、什么是电平?

在数字电路中,电平是用来表达数字信号高低状态的一种方式,电平是用一段电压范围来表示的。

在数字电路中,只有两个电平,即高电平和低电平,又分别称作“1”和“0”。C语言中,二进制位的

数值也是1和0,刚好对应电路中的电平。STM32单片机供电电压VDD =3.3V。

0V-0.7V:0         2.2V-3.3V:1

3、STM32 GPIO简介

GPIO有两大类模式:输出模式,输入模式

  1. 输出模式:控制IO引脚输出高低电平,可以用来控制外部设备或者输出数据。比如控制LED灯,继电器。
  2. 输入模式:用来读取IO引脚的电平或者电压,也可以用来接收数据。比如,采集按键信息,外部的模拟电压。
  3. 带FT标志的IO可以输入5V电压而不损坏,其他IO输入电压不能超过VDD (3.3V)
  4. 单片机工作的总电流不能超过150mA,单个O引脚电流最大为20mA。
  5. 上下拉电阻典型值为40K,最小值为30K,最大值为50K

我们使用的,GPIOA、GPIOB。16+16=32个GPIO口

PA0-PA7:八个口在一起,这八个是模拟口,可以用来采集模拟信号

PA8-PA15:八个口在一起,并不能采集模拟,这边是数字信号的口

4、GPIO基本结构介绍

位的基本结构:(单片机的端口它是由16位组成的)

输入驱动器:有上下拉电阻与浮空,以及模拟输入。触发器外部信号滤波(抖动变直线),如果要

采集模拟信号,把触发器关掉。

复用功能输入:其实也是一种数字信号,连接到单片机内部模块(数字模块),上面模拟输入是模

拟模块,数字模块它是可以直接通过触发器过来。复用功能:第二功能。

复用:多路开关123,通过寄存器拨到哪路,就是那个功能。

输入数据寄存器:通过这个电路,判断出是高电平还是低电平。高电平寄存器写1,低电平写0。最后读出数据。

输入驱动器:推挽、开漏,复用

复用:复用功能输出也是指的是数字复用功能

比如说我们输出一个PWM(脉宽调制,控制电机速度)本质上其实是一个方波,也是连接到定时器的(定时器模块连接到此)

读/写普通输出口:一个是直接输出数据寄存器进行操作,读/写,往输出数据寄存器写1,单片机这个I/O引脚就会输出3.3V。

写入位设置/清除寄存器:对单片机引脚位进行操作,往设置寄存器写1,输出数据寄存器它也会变成1。往清除寄存器写1,输出数据寄存器它也会变成0。设置清除同时写,按照设置寄存器写1处理。两个16位的寄存器拼接在一起成32位。

注意:输入输出两个寄存器是特别重要,输入数据寄存器,设置/清除寄存器。只要改变它中的内容,就会改变单片机引脚上的内容。

(推挽)输出控制:根据我们输出寄存器中的数据控制这两个mos管。设置寄存器1,输出控制给出一个信号,让下mos管关闭,上mos管打开,VDD通过上mos管,I/O引脚输出高电平。输出寄存器写0,则关上mos管,开下mos管,I/O引脚输出低电平。

注意两个箭头,如何到I/O口的然后输出,与从I/O口出来然后进入单片机。

开漏输出模式:上mos管永远关闭,只能控制下mos管。只能输出低电平。如果你要想输出高电平,只能在单片机引脚外部接一个上拉电阻,连接到VDD(引脚FT5v,否则3.3v)。如下图。

开漏输出好处:正常不能输出5V(只能3.3V),通过开漏外接的上拉电阻电源,就可以输出5V。

很多设备都是5V的,如果我们想要控制这些设备就得用开漏模式。

高电平:控制由P-MOS与PNP

低电平:控制由N-MOS与NPN

5、GPIO的8种工作模式

输入:模拟,上拉,下拉,浮空

输出:推挽,开漏,复用推挽,复用开漏

复用连接的是内部模块(如定时器)

输出模式:高低电平这个切换速度(为什么要三种输出?):如果输出一个周期性的方波,那么你这个方波是不可能超过10MHZ的,单片机它这个输出速度越高,干扰就越大,工功耗也是越大的。一般把速度降低下来低功耗与抗干扰。

复用功能输出的时候,输出寄存器是不可控制。(ODR叫做数据输出寄存器,往里面写数据改变单片机引脚的电平)

输入:输入不存在什么速度配置的,单片机外部电路的信号能有不高,太快了单片机处理不过来就没有意义(MODE:00)。单片机默认情况下就是浮空输入。

ODR数据输出寄存器0,下拉输入(下拉电阻)。ODR数据输出寄存器1,上拉输入(上拉电阻)。ODR数据输出寄存器:区分上拉电阻和下拉电阻。

注意:在开漏模式时,对输入数据寄存器的读访问可得到I/O状态(既可以读也可以写,不需要切换到输入模式。推挽不能读)

宏定义:

下拉和上拉都一样,通过输出数据寄存器ODR的0/1,判断上拉还是下拉。1上拉,0下拉。

6、GPIO的寄存器

        GPIOx是一个结构体指针,指向GPIOx寄存器组的首地址,其成员寄存器组中的寄存器一一对应比如访问输出数据寄存器,可以这样GPIOA->ODR。

形成一个映射关系(类型转换,寄存器地址转成结构体成员),访问成员就是访问寄存器。

7.GPIO寄存器

端口配置低寄存器(GPIOx_CRL)

一个引脚需要四位来配置(CNFy[1:0]与MODEy[1:0]):配置PA0

端口(引脚):一个引脚需要四位来配置,16个引脚是需要16*4=64,我们单片机它是32位的,所以说它的寄存器是32位的,你需要两个寄存器来进行配置。

因此需要高低配置寄存器,高8位端口,低8位端口。

宏定义左移右移。

低八位;

PA0是0000,如果要想配置其他几位,如PA2,你只需要把0001就左移8(2*4)位。如PA3,0001就左移12(3*4)位.

高八位:(端口配置高寄存器 端口配置高寄存器(GPIOx_CRH) )

如果要想配置其他几位,如PA14,只需要左移8((14-8)*4=24)位。

端口输入数据寄存器(GPIOx_IDR)

你把工作模式配置成输入,只需要把这里面的数据读出来,就可以知道单片机引脚中的数据了。

PA0数据:IDR0

PA1数据:IDR1

端口输出数据寄存器(GPIOx_ODR)

要输出什么电平,就往这个数据寄存器对应的位写01

比如说现在是是GPIOA,只要往这一位( ODR10)写1,表示PA10输出高电平。

( ODR8)写1,表示PA8输出高电平。( ODR8)写0,表示PA8输出低电平。

端口位设置/ 清除寄存器(GPIOx_BSRR)

BS,16位是控制电平输出:只要写1就是电平

BR,16位是控制电平输出:只要写1就是电平

注:如果同时设置了BSy和BRy的对应位,BSy位起作用

端口位清除寄存器(GPIOx_BRR) 

1:清除对应的ODRy位为0        同上BR功能,只要写1就是电平。

往BR13写1,PA9就低电平。

注意:

端口位设置/ 清除寄存器(GPIOx_BSRR)和端口位清除寄存器(GPIOx_BRR) 都是间接操作端口输出数据寄存器(GPIOx_ODR)。

总结

一般直接操作输出数据寄存器(GPIOx_ODR)

常用的就四个寄存器:端口配置低寄存器(GPIOx_CRL),端口配置高寄存器(GPIOx_CRH),端口输入数据寄存器(GPIOx_IDR),端口输出数据寄存器(GPIOx_ODR)

明白如何输出01和读取单片机引脚的电平。

外部中断:通过单片机引脚进行的,一种输入,可以产生中断。

第4-4节:点亮LED_寄存器版本

代码逻辑框图

1.宏定义8种模式

GPIO.h

#ifndef __GPIO_H
#define __GPIO_H

//上拉下拉都是0X08,由ODR寄存器区分。ODR:0下拉,1上拉。
#define GPIO_INPUT_ANALOG 	0X00
#define GPIO_INPUT_NOPULL 	0X04
#define GPIO_INPUT_PULLUP 	0X08
#define GPIO_INPUT_PULLDOWN 0X08

#define GPIO_OUTPUT_PP 		0X00
#define GPIO_OUTPUT_OD 		0X04
#define GPIO_OUTPUT_AF_PP 	0X08
#define GPIO_OUTPUT_AF_OD 	0X0C

#define GPIO_SPEED_10MHZ 	0X01
#define GPIO_SPEED_2MHZ 	0X02
#define GPIO_SPEED_50MHZ 	0X03

#endif

LED初始化函数

第一步还是打开时钟

RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

紧接着进行配置(配置寄存器两个,高八位、低八位)

我使用的GPIOA4是低八位。

推挽+2MHZ,GPIOA4 = 4*4 = 16 左移16位

GPIOA->CRL |= (GPIO_OUTPUT_PP + GPIO_SPEED_2MHZ)<<16;

设置好了之后,设置一个它上电默认的电平

GPIOA->ODR |= 1<<3;

总:

void Led_Init(void)
{
	RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
	
	GPIOA->CRL &= 0XFFF0FFFF;//初始化第五个端口PA4
	GPIOA->CRL |= (GPIO_OUTPUT_PP + GPIO_SPEED_2MHZ)<<16;
	
	GPIOA->ODR |= 1<<4;
}

使用端口配置低/高寄存器时,最好先清除对应GPIOX的引脚的配置信息。(防止因为默认I/O端口0100导致没配置成,如:因为默认0100,你要配置成0001,GPIOA->CRL |= 0001。理想结果为0001,但是因为 1 | 0 =1,最终结果为0101)
初始化端口代码:
你要配置GPIOA的第5个引脚(也就是GPIOA4,第八位用CRL)
GPIOA->CRL &= 0XFFF0FFFF;//初始化端口

main.c

#include "main.h"
#include "stm32f10x.h"                  // Device header
#include "Led.h"
#include "Delay.h"


int main(void)
{
	Led_Init();
	while(1)
	{
		GPIOA->ODR |= 1<<3;
		Delay_ms(100);
		GPIOA->ODR &= ~(1<<3);
		Delay_ms(100);
	}
}

复现错误之处(保留.c/.h对照用户手册):

1.一时想不起来使能寄存器应该写。对照手册有规律可言,还是不够熟悉

	RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

2.宏定义,手册是2进制,而自己写是0X。应该进行2到16进制的转换.

#ifndef __GPIO_H
#define __GPIO_H

#define INPUT_FLOATING	0X04
#define INPUT_PULLUP	0X08
#define INPUT_PULLDOWN	0X08
#define ANALOG_INPUT	0X00

#define OPEN_DRAIN_OUTPUT	0X04
#define PUSH_PULL_OUTPUT	0X00
#define PUSH_PULL_REUSE_FUNCTION	0X08
#define OPEN_DRAIN_REUSE_FUNCTION	0X0C

#define SPEED_10MHz	0X01
#define SPEED_2MHz	0X02
#define SPEED_50MHz	0X03

#endif

3.

	GPIOA->CRH &= ~(0XFFFF0FFF); //错误写法,习惯性加了个~

    GPIOA->CRH &= ~(0XFFFF0FFF); //正确写法,与0直接就行,不是用1

第5-1节:STM32按键检测算法

单片机它可以控制外部的设备,还可以读取外部设备的状态。如:按键是否被按下。如何判断呢?

单片机它只能输入模拟信号,还有就是输入高低电平这种数字信号

按键它其实是一个开关,按下去跟没按下去两种转态(读取数字信号),这种数字信号如何读呢?单片机它是可以直接读数据数字信号的,只需要根据这个引脚的高低电平,就可以判断出按键是否被按下。

归根结底就是判断高电平和低电平

编程:一般可以用if语句来判断。

不使用长时间延迟,改而在多次判断,防止抖动误判。

代码逻辑框图:

1.

2.

3.

4.

5.

第5-3节: 按键检测-寄存器

STM32 初始化步骤

  • 开启GPIO时钟
  • 配置GPIO方向INPUT /OUTPUT
  • 配置GPIO输出输出模式

输入:模拟,浮空,上下拉

输出: PP,OD,AFPP,AFOD

        寄存器里面上下拉的设置,由数据寄存器GOID->ODR决定,数据寄存器对应的位为1,就是上拉,因为你输出高电平嘛,上拉才会是高电平。下拉,数据寄存器必须输出零。

上拉: GOID->ODR |= 1<<n
下拉: GOID->ODR &= ~(1<<n)

如图:

GOID->ODR控制上下拉,路线。框图线路:

程序流程框图:

算法代码Key.c

#include "main.h"
#include "Key.h"
#include "stm32f10x.h"                  // Device header
#include "GPIO.h"
#include "Led.h"

//KEY1: PB1 KEY2:PB11
#define Key1 (GPIOB->IDR&(1<<1))
#define Key2 (GPIOB->IDR&(1<<11))

#define  KEY1_VALUE		0X0001
#define  KEY2_VALUE		0X0002
#define  KEY3_VALUE		0X0003	//双按

#define  KEY_CHANG_MODE		0X8000	//长按标志

void Key_GPIO_Init(void)
{
	RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
	
	GPIOB->CRL &= 0XFFFFFF0F;//初始化PB1、PB11
	GPIOB->CRH &= 0XFFFF0FFF;
	GPIOB->CRL |= (GPIO_INPUT_PULLUP)<<4;//PB1
	GPIOB->CRH |= (GPIO_INPUT_PULLUP)<<12;//PB11
	
	GPIOB->ODR |= 1<<1;//PB1上拉
	GPIOB->ODR |= 1<<11;//PB11上拉
}

/*读取按键*/
uint16_t ReadKey(void)
{
	static uint32_t Key_Cnt1 = 0,Key_Cnt2 = 0,Key_Cnt3 = 0;
	uint16_t KeyValue = 0;
	
	/*按键1Key1*/
	if(Key1 == 0)
	{
		Key_Cnt1++;
		
		if(Key_Cnt1 >= 400) //5ms*400=2s 长按
		{
			KeyValue = KEY1_VALUE + KEY_CHANG_MODE;
			Key_Cnt1 = 400;
		}
	}
	else
	{
		if(Key_Cnt1 <= 100) //短按
		{
			if(Key_Cnt1 >= 10)
			{
				KeyValue = KEY1_VALUE;
			}
		}
		Key_Cnt1 = 0;
	}
	
	/*按键2Key2*/
	if(Key2 == 0)
	{
		Key_Cnt2++;
		
		if(Key_Cnt2 >= 400) //5ms*400=2s 长按
		{
			KeyValue = KEY2_VALUE + KEY_CHANG_MODE;
			Key_Cnt2 = 400;
		}
	}
	else
	{
		if(Key_Cnt2 <= 100) //短按
		{
			if(Key_Cnt2 >= 10)
			{
				KeyValue = KEY2_VALUE;
			}
		}
		Key_Cnt2 = 0;
	}
	
	/*同时按键1,2*/
	if((Key1 == 0)&&(Key2 == 0))
	{	
		Key_Cnt1 = 0;
		Key_Cnt2 = 0;
		Key_Cnt3++;
		
		if(Key_Cnt3 >= 400) //5ms*400=2s 长按
		{
			KeyValue = KEY3_VALUE + KEY_CHANG_MODE;
			Key_Cnt3 = 400;
		}
	}
	else
	{
		if(Key_Cnt3 <= 100) //短按
		{
			if(Key_Cnt3 >= 10)
			{
				KeyValue = KEY3_VALUE;
			}
		}
		Key_Cnt3 = 0;
	}
	
	return KeyValue; //返回被按下的按键的编码
}

/*按下处理*/
void KeyDeal(uint16_t KeyValue)
{
	uint16_t mode = KeyValue & 0x8000;//取出长短按标志位;&1,保留标志位
	
	switch (KeyValue & 0x7fff)//取出长短按真正的编码,按键编码(判断是按下了那个按键)
	{
		case KEY1_VALUE:
			if(mode)
			{
				//长按
				 LED1_OFF();
			}
			else
			{
				//短按
				LED1_ON();
			}
		break;
			
		case KEY2_VALUE:
			if(mode)
			{
				//长按
				LED2_OFF();
			}
			else
			{
				//短按
				LED2_ON();
			}
		break;
			
		case KEY3_VALUE:
			if(mode)
			{
				//长按
				LED3_OFF();
			}
			else
			{
				//短按
				LED3_ON();
			}
		break;
	}
}

void KeyScanf(void)
{
	static uint16_t KeyValue = 0,Pre_keyValue = 0;
	
	KeyValue = ReadKey();
	
	if(KeyValue != Pre_keyValue)//此次按键值与上次按键值的
	{
		Pre_keyValue = KeyValue;
		KeyDeal(KeyValue);
	}
}

总结:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
嗨!对于学习STM32寄存器编程,你可以按照以下步骤进行: 1. 了解寄存器的基本概念:寄存器是用来存储数据的硬件组件,它们在微控制器中起着重要的作用。了解寄存器的种类和功能是学习STM32寄存器编程的基础。 2. 确定你所使用的STM32系列微控制器型号:每个STM32系列微控制器都有自己的寄存器集合和功能。你需要确定你所使用的型号,并下载相关的参考手册。 3. 下载STM32参考手册:在STMicroelectronics官网上,你可以找到针对特定型号的STM32参考手册。这些手册详细描述了每个寄存器的功能、配置方法和寄存器地址等重要信息。 4. 学习寄存器编程技巧:在学习STM32寄存器编程之前,你需要掌握C或者汇编语言编程知识。了解如何读写寄存器、设置位字段和使用寄存器的位操作是非常重要的。 5. 编写代码:根据参考手册中提供的信息,你可以编写代码来配置和操作寄存器。这样可以实现对特定功能或外设的控制。 6. 测试和调试:一旦编写完代码,你可以将其下载到STM32微控制器上,并通过调试器或串口输出等方式验证是否实现了预期的功能。如果有问题,可以根据调试信息进行排查和修复。 请注意,寄存器编程是低级别的编程方法,需要更多的手动配置和对硬件的了解。如果你是STM32初学者,可能更容易使用标准库或CubeMX等工具来进行开发,这些工具可以简化硬件配置过程。一旦你对STM32有了更深入的了解,再尝试寄存器编程也是很有意义的。祝你学习愉快!如有问题,欢迎继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值