首页 >> 大全

第一天与单片机基础知识:V1_stm32(寄存器版本)

2023-11-23 大全 34 作者:考证青年

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

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

用户手册:

数据手册:

关键参数C8T

48,64K,LQFR

1、STM32容量划分

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

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

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

2、STM32时钟系统

如何看?

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

关键:

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

输入:

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

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

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

输出:

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

通向:核心存储器和DMA;

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

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

2.写一个延迟函数

通向:CP的内核72MHZ

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

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

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

再往下APB1走,之前选两分频(分频系数不为1)要乘以二,也就是36×2=72,给到定时器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单片机的(接地)

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

3、STM32内部模块图

4、STM32封装(外形)图

:封装LQFP,引脚48

5、STM32引脚功能

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

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

图1:

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

图2:

1引脚,备用电源电路

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

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

3,4引脚,晶振电路:

两无极电容22pf+晶振32.

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供电,通过降压芯片(-3.3)将usb的5v降到3.3v供电

保险丝,电流过大的保护

28脚,验证工作指示灯

下载程序1

引脚(三根线)

下载与调试

下载器插座

D4保护,防止接反

一般ST-LINK接SWCLK/SWDIO

时钟线:

数据线:

地线GND

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

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

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

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

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

接单片机

接USB

启动配置电路

一般直接BOOT1置0

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

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

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

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

参考:

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

寄存器和标准库

点击

选中即可

第4-1节:STM32 GPIO输入输出端口 主要内容: 1、什么是GPIO?

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

2、什么是电平?

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

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

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

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

3、STM32 GPIO简介

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

输出模式:控制IO引脚输出高低电平,可以用来控制外部设备或者输出数据。比如控制LED灯,继电器。输入模式:用来读取IO引脚的电平或者电压,也可以用来接收数据。比如,采集按键信息,外部的模拟电压。带FT标志的IO可以输入5V电压而不损坏,其他IO输入电压不能超过VDD (3.3V)单片机工作的总电流不能超过150mA,单个O引脚电流最大为20mA。上下拉电阻典型值为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寄存器 端口配置低寄存器()

一个引脚需要四位来配置(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)位.

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

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

端口输入数据寄存器()

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

PA0数据:IDR0

PA1数据:IDR1

端口输出数据寄存器()

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

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

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

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

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

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

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

端口位清除寄存器()

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

往BR13写1,PA9就低电平。

注意:

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

总结

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

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

明白如何输出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;

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

我使用的是低八位。

推挽+2MHZ, = 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;//初始化第五个端口PA4GPIOA->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个引脚(也就是,第八位用CRL)

GPIOA->CRL &= ;//初始化端口

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 初始化步骤

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

输出: PP,OD,AFPP,AFOD

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

上拉: GOID->ODR |= 1ODR &= ~(1ODR控制上下拉,路线。框图线路:

程序流程框图:

算法代码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、PB11GPIOB->CRH &= 0XFFFF0FFF;GPIOB->CRL |= (GPIO_INPUT_PULLUP)<<4;//PB1GPIOB->CRH |= (GPIO_INPUT_PULLUP)<<12;//PB11GPIOB->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);}
}

总结:

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了