STMCU小助手
发布时间:2022-2-2 20:00
|
ARM处理器家族
想要操作stm32的外设通常可以使用寄存器或者stm32库,在当今stm32库已经十分成熟,并且操作十分简单。 寄存器:寄存器则是内置于各个 IP 外设中,是一种用于配置外设功能的存储器,就是一种内存,并且有相对应的地址。 GPIO:通用输入输出接口 按键:通过输入电平的高低感知来得到按键的按下与否。 led灯:通过输出高低电平控制led的亮与灭。 GPIO也可以作为串口数据的收发管脚或AD接口等复用功能,根据数据手册配置相应的功能寄存器。 2 }* {) Z8 f/ q! H! r! RGPIO寄存器MODER 32位两位为一组,分为了16个,分别对应每一组GPIO的16个 管脚。如PA0-PA15。 点灯:学习嵌入式开发最开始的操作就是点灯,以下使用寄存器操作和库函数操作来点灯。 寄存器:从技术参考手册中可以找到ODR 寄存器的地址偏移是: 0CH,这个偏移地址是基于端口的起始地址而言的。在 STM32 中,每个外设都有一个起始地址,叫做外设基址,外设的寄存器就以这个基地址为标准按照顺序排列,跟结构体里面的成员差不多。 假设我们需要点亮的灯是由GPIOB-0这个管脚去控制的,找到GPIOB对应的基地址,再加上ODR的偏移地址就可以去实现输出功能。 ![]()
- C) I" |7 `: @9 W% D, ^$ f3 b ![]() 器了:1 Z5 v! k0 d, L& T - O! S0 y/ z) F: Y #define GPIOB_ODR *(volatile unsigned long *)0x40010C0C 有了这个寄存器定义,我们就可以直接操作 GPIOB_ODR 了。虽然配置了 ODR 寄存器,但是这个时候还不能点亮 LED ,因为 STM32 的 IO 口还要配置方向,这个由端口配置寄存器来控制。端口配置寄存器分为高低两个,每 4bit 控制一个 IO 口,所以端口配置低寄存器: CRL 控制这 IO 口的低 8 位,端口配置高寄存器: CRH控制这 IO 口的高 8位。在 4 位一组的控制位中, CNFy[1:0] 用来控制端口的输入输出,MODEy[1:0] 用来控制输出模式的速率,即输出时, IO 电平翻转的速度。+ X. P0 d, {! p U. D7 w" F3 t# f3 [ 输入有三种模式,输出有 4 中模式,我们在控制 LED 的时候选择通用推挽输出。 A& k8 r* b. w 输出速率有三种模式: 2M、10M 、50M,这里我们选择 2M。 8 _+ c, B7 N V1 ^ u" R- ` 同 GPIOB_ODR 一样,我们也可以算出 GPIO_CRL 的地址为: 0x40010C00。那么设置PB0 为通用推挽输出,输出速率为 2M 的代码则如下所示:' J4 k/ [7 _9 r6 T4 g- S #define GPIOB_CRL *(volatile unsigned long *)0x40010C00* ?# t- h/ q8 q9 r/ T Y+ |" v9 _ 2 P% d- z2 j% l // 配置 PB0 为通用推挽输出,输出速率为 2M GPIOB_CRL = (2<<0) | (0<<2);
# K( ^9 R+ j1 T0 ^1 x7 P( ?当我们设置了 IO 口的方向,并在相应的输出寄存器里面输入了值的时候,以为现在总算可以点亮 LED 了吧,其实还差最后一步。, c2 `2 u; U! _' n: t STM32 外设很多,为了降低功耗,每个外设都对应着一个时钟,在系统复位的时候这些时钟都是被关闭的,如果想要外设工作,必须把相应的时钟打开。8 z4 L0 R5 s1 l2 k3 l& k STM32 的所有外设的时钟由一个专门的外设来管理,叫 RCC(reset and clockcontrol ), RCC 在 STM32 中文参考手册的第六章。 5 K: m( j( F- c- O. q* u% r2 b! T: O N STM32 的外设因为速率的不同,分别挂载到三条总系上: AHB 、APB2 、APB1 ,APB为高速总线, APB2 次之, APB1 再次之。8 J, L; y) @- M: ` 所有的 IO 口都挂载到 APB2 总线上,属于高速外设。时钟由 APB2 外设时钟使能寄存器 (RCC_APB2ENR) 来控制,其中 PB 端口的时钟由该寄存器的位 3 写 1 使能。
同 ODR 和 CRL,我们可以算出 RCC_APB2ENR 的地址为: 0x40021018。那么使能PB 口的时钟代码则如下所示:/ k: d3 B3 j( T8 H2 Z* u' R& H#define RCC_APB2ENR *(volatile unsigned long *)0x40021018; H) q6 Q2 v% s9 P: L4 @8 W# ~ // 开启端口 B 时钟; w% G4 g9 U& W6 v5 J RCC_APB2ENR |= 1<<3;; }6 ^. w$ F9 @' u6 T 我们虽然开了端口时钟,那这个时钟到底是多大?时钟到底是从哪里来的? 如果我们用的是库,那么有个库函数 SystemInit ,会帮我们把系统时钟设置成 72M 。现在我们没有使用库,那现在时钟是多少?, m+ u$ Q6 L/ w. W2 I/ {, `; b 答案是 8M,当外部 HSE 没有开启或者出现故障的时候,系统时钟由内部低速时钟 LSI 提供,现在我们是没有开启 HSE,所以系统默认的时钟是 LSI=8M 。 如果你想自己先尝鲜,那么看 RCC 外设中的:时钟控制寄存器 (RCC_CR) 和时钟配置寄存器(RCC_CFGR) 这两个寄存器即可。 控制了电平,配置了方向,开启了时钟,经过这三步,我们总算可以控制一个 LED了。 现在我们完整组织下用 STM32 控制一个 LED 的代码:9 [& p1 p" h( ?4 D: C2 G" o4 o, n0 D
这种就是比较愚蠢的配置方式,每次配置一个外设就去从参考手册中去找,浪费时间且容易出错,但新手入门是需要理解并且操作学习一下的。 如果要以寄存器配置的话,会将所有的寄存器位置都先定义好,这样就不用一个个去找。实际开发过程中很少使用寄存器操作,一般都是库函数的操作,下面介绍库的操作。 . U. t2 o6 I* ]# pkeil的stm32环境自行去百度配置,篇幅太长就不写出了。 固件库分析:看看每个文件的作用是什么,这对我们能否清晰的调用库函数编程非常重要。 STM32 由 Cortex-M3 内核和内核之外的各种外设组成,库在编写的时候也遵循这中组成结构,把代码分成两大部分,一种是操作内核外设的, 另外一种是内核之外的外设,为了听起来不那么绕,下面我们把内核之外的外设用处理器外设来代替。
![]()
直接写库函数操作了:
+ z% U( C4 x. V2 ] Mode和Speed相关结构体也是被设定好的,可以自行查找这里就不写出来了,直接赋值就可以了,十分方便并且相对于寄存器操作便于理解。 GPIO_Init(GPIO_TypeDef *, GPIO_InitTypeDef *);第一个参数,说明它将要对GPIOB 端口进行初始化。初始化的配置以第二个参数 GPIO_InitStructure 结构 体的成员值为准。这个结构体的成员,我们在调用GPIO_Init()前,已对它们 赋予了控制参数。 8 H9 y" N; j( X/ u! [& v2 s8 K( q* e" y- T ! h5 K) M. ^. _- g ) E/ H" I# m; }2 S* ^( j' K |
实战经验 | 基于STM32G0B1的全桥移相+同步整流PWM解决方案
DMA 原理从 0 到 1:为什么它能解放 CPU?一文讲透 DMA 的工作流程
【福利三:逢7发帖赢大礼】8、基于STM32G070板子的Ymodem串口协议IAP Bootloader开发
【福利三:逢7发帖赢大礼】7、基于STM32G070板子TF卡的FATFS移植
【福利三:逢7发帖赢大礼】6、基于STM32G070板子spiflash的FATFS移植
【福利三:逢7发帖赢大礼】5、基于STM32G070板子IRDA红外遥控接收解码
【福利三:逢7发帖赢大礼】4、基于STM32G070板子SPI flash 移植SFUD库
【福利三:逢7发帖赢大礼】基于STM32G070板子的uart shell移植设计
【福利三:逢7发帖赢大礼】3、基于STM32G070板子的YModem串口协议通信
【福利三:逢7发帖赢大礼】2、基于STM32G070板子的OLED移植U8G2库
微信公众号
手机版