我们观察到一个社会越来越依赖于机器,但同时却更加无效率地使用它们。-道格拉斯·鲁斯科夫(Douglas Rushkoff)
这个短语应该成为每个程序员的动力。毕竟,是您决定机器如何使用其资源。但是从一开始,一个人就将自己的决定权委托给第三方,以换取简便的方式。在问我有“立方体”时文章的好处之前,先问自己为什么“立方体”为我决定。STM32第1部分:STM32 基础知识第2部分:初始化因此,让我们继续我们的冒险。我们已经编写了初始化脚本,找出了链接器和编译器。现在该使LED闪烁了。在本文中,我们将介绍RCC和GPIO块的基础知识,并添加几个标头,这些标头将在以后的项目中使用。走。RCC-复位和时钟控制,一块寄存器,用于控制处理器和外设的时钟。此设备控制几个时钟源:HSI(内部高速),HSE(外部高速),PLL / PLLSAI / PLLI2S(定相环路锁定)。当然,如果正确配置了RCC块寄存器,则处理器可以使用所有这三个时钟源。我们将在下一篇文章中详细讨论。GPIO-通用输入和输出。为LED闪烁而创建的寄存器块,即,它是控制微米级分支(进入现实世界的门户)的块。与熟悉的Atmega328p(Arduino)不同,stm32提供了更广泛的腿部定制。例如,大型排水阀上的排水排水脚功能不可用。另外,可以配置对uk升高腿部电压的速度的控制。所以,我们已经眨眼了,是的。首先,我们需要两个标头,在其中描述这些寄存器的结构。因此,我们获得了参考手册并进行了研究,以查找所需的信息。我致力于STM32F746NG-发现,因此该板的所有代码。我们需要的第一件事是这些块的地址,基本的。让我们从RCC块开始,并创建rcc.h标头。在其中,我们将创建宏RCC_BASE,并描述寄存器块的结构,并创建指向该结构的指针。rcc.h#define RCC_BASE 0x40023800
typedef struct
{
volatile unsigned int CR;
volatile unsigned int PLLCFGR;
volatile unsigned int CFGR;
volatile unsigned int CIR;
volatile unsigned int AHB1RSTR;
volatile unsigned int AHB2RSTR;
volatile unsigned int AHB3RSTR;
unsigned int RESERVED0;
volatile unsigned int APB1RSTR;
volatile unsigned int APB2RSTR;
unsigned int RESERVED1[2];
volatile unsigned int AHB1ENR;
volatile unsigned int AHB2ENR;
volatile unsigned int AHB3ENR;
unsigned int RESERVED2;
volatile unsigned int APB1ENR;
volatile unsigned int APB2ENR;
unsigned int RESERVED3[2];
volatile unsigned int AHB1LPENR;
volatile unsigned int AHB2LPENR;
volatile unsigned int AHB3LPENR;
unsigned int RESERVED4;
volatile unsigned int APB1LPENR;
volatile unsigned int APB2LPENR;
unsigned int RESERVED5[2];
volatile unsigned int BDCR;
volatile unsigned int CSR;
unsigned int RESERVED6[2];
volatile unsigned int SSCGR;
volatile unsigned int PLLI2SCFGR;
volatile unsigned int PLLSAICFGR;
volatile unsigned int DCKCFGR1;
volatile unsigned int DCKCFGR2;
} RCC_Struct;
#define RCC ((RCC_Struct *) RCC_BASE)
让我们对GPIO寄存器块进行相同的操作,只是不使用指针,然后添加另一个宏GPIO_OFFSET。在下面谈论它。gpio.h#define GPIO_BASE 0x40020000
#define GPIO_OFFSET 0x400
typedef struct
{
volatile unsigned int MODER;
volatile unsigned int OTYPER;
volatile unsigned int OSPEEDR;
volatile unsigned int PUPDR;
volatile unsigned int IDR;
volatile unsigned int ODR;
volatile unsigned int BSRR;
volatile unsigned int LCKR;
volatile unsigned int AFR[2];
} GPIO_Struct;
让我们看看这里有什么问题,为什么我们需要结构。事实是,我们可以通过指向结构的指针访问寄存器,我们不需要初始化它,因为它实际上已经存在。例如:RCC->AHB1ENR = 0;
这样可以节省内存空间,有时甚至根本不需要它。但不是要保存今天。因此,我们已经准备好两个头文件,仍然需要学习如何在这些寄存器的帮助下进行跳转和创建 int main();
。这很简单,但并不完全。在STM32中,要访问寄存器块,我们必须首先对其施加时钟,否则数据将到达它。我不会深入研究轮胎的结构,只是照原样说。块被放置在不同的轮胎上。我们的设备位于AHB1总线上。也就是说,我们需要启用AHB1总线上的特定端口,在我的情况下是端口I。对于启动器,我们当然需要主要功能。让我们稍微更新一下启动文件,然后向其中添加int main();。然后创建main.c文件本身extern void *_estack;
void Reset_Handler();
void Default_Handler();
int main();
void NMI_Handler() __attribute__ ((weak, alias ("Default_Handler")));
void HardFault_Handler() __attribute__ ((weak, alias ("Default_Handler")));
void *vectors[] __attribute__((section(".isr_vector"), used)) = {
&_estack,
&Reset_Handler,
&NMI_Handler,
&HardFault_Handler
};
extern void *_sidata, *_sdata, *_edata, *_sbss, *_ebss;
void __attribute__((naked, noreturn)) Reset_Handler()
{
void **pSource, **pDest;
for (pSource = &_sidata, pDest = &_sdata; pDest != &_edata; pSource++, pDest++)
*pDest = *pSource;
for (pDest = &_sbss; pDest != &_ebss; pDest++)
*pDest = 0;
main();
while(1);
}
void __attribute__((naked, noreturn)) Default_Handler(){
while(1);
}
现在我们自己创建main.c文件 在文件中,我尝试将代码注释中的所有内容都绘制出来,因此我们阅读并深入研究了它。如果您有任何疑问,请在下面的评论中写我会回答。#include "rcc.h"
#include "gpio.h"
int main()
{
RCC->AHB1ENR |= (1<<8);
volatile GPIO_Struct *GPIOI = (GPIO_Struct *)(GPIO_BASE + (GPIO_OFFSET*8));
GPIOI->MODER |= (1<<2);
GPIOI->OTYPER &= ~(1<<1);
GPIOI->OSPEEDR |= (2<<2);
while(1){
for(volatile int i = 0; i < 1000000; i++){
}
GPIOI->ODR ^= (1<<1);
}
return 0;
}
现在,我们必须将一个项目放在一起并放在微米上进行验证。我在Github上发布了一个存储库,所有要做的就是运行Make实用程序。make
谢谢大家的关注,在下一篇文章中,我们将更多地讨论RCC单元以及如何使用它。