STM32第3部分:第一个项目

我们观察到一个社会越来越依赖于机器,但同时却更加无效率地使用它们。-道格拉斯·鲁斯科夫(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()
{
    //  GPIO I ,   8 ( )
    RCC->AHB1ENR |= (1<<8);

    //     
    //          
    //      
    volatile GPIO_Struct *GPIOI = (GPIO_Struct *)(GPIO_BASE + (GPIO_OFFSET*8));  

    //     ""
    GPIOI->MODER |= (1<<2);

    //   ,   , push-pull,   .
    //   push pull
    GPIOI->OTYPER &= ~(1<<1);

    //      ,       
    GPIOI->OSPEEDR |= (2<<2);

    //   
    while(1){
        for(volatile int i = 0; i < 1000000; i++){
            // 
        }

        // ,   1   0  .
        GPIOI->ODR ^= (1<<1);
    }

    return 0;
}


现在,我们必须将一个项目放在一起并放在微米上进行验证。在Github上发布了一个存储库,所有要做的就是运行Make实用程序。
make


谢谢大家的关注,在下一篇文章中,我们将更多地讨论RCC单元以及如何使用它。

All Articles