加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板

(2014-09-19 14:42:33)
标签:

51

arm

it

开发板

神舟

分类: 神舟51单片机开发板

   5.7.1  硬件原理图分析

    例程硬件原理图说明

现在,知道可以访问处理器所有的寄存器了,我们可以通过改写这些寄存器的值,控制芯片做不同的功能和操作。

下面我们正式写个例程来感受一下.这个例程用C语言来修改这个内存地址的内容,从而控制寄存器,通过寄存器控制STM32芯片的PD4管脚使得一个灯亮和灭的。

原理图如下,上面已经有介绍:

http://s5/bmiddle/002aCJOcgy6Ma8yhdRy44&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />      

例程main.c源代码(可以直接运行):

以下是main.c的源文件,读者可以直接粘贴编译:

 #define     __IO    volatile            

typedef unsigned           int uint32_t;

typedef __IO uint32_t  vu32;

typedef unsigned short     int uint16_t;

#define GPIO_Pin_0                 ((uint16_t)0x0001) 

#define GPIO_Pin_1                 ((uint16_t)0x0002) 

#define GPIO_Pin_2                 ((uint16_t)0x0004) 

#define GPIO_Pin_3                 ((uint16_t)0x0008) 

#define GPIO_Pin_4                 ((uint16_t)0x0010) 

#define GPIO_Pin_5                 ((uint16_t)0x0020) 

#define GPIO_Pin_6                 ((uint16_t)0x0040) 

#define GPIO_Pin_7                 ((uint16_t)0x0080) 

#define GPIO_Pin_8                 ((uint16_t)0x0100) 

#define GPIO_Pin_9                 ((uint16_t)0x0200) 

#define GPIO_Pin_10                ((uint16_t)0x0400) 

#define GPIO_Pin_11                ((uint16_t)0x0800) 

#define GPIO_Pin_12                ((uint16_t)0x1000) 

#define GPIO_Pin_13                ((uint16_t)0x2000) 

#define GPIO_Pin_14                ((uint16_t)0x4000) 

#define GPIO_Pin_15                ((uint16_t)0x8000) 

#define GPIO_Pin_All               ((uint16_t)0xFFFF) 

#define RCC_APB2Periph_AFIO              ((uint32_t)0x00000001)

#define RCC_APB2Periph_GPIOA             ((uint32_t)0x00000004)

#define RCC_APB2Periph_GPIOB             ((uint32_t)0x00000008)

#define RCC_APB2Periph_GPIOD             ((uint32_t)0x00000020)


typedef struct

{

  __IO uint32_t CRL;

  __IO uint32_t CRH;

  __IO uint32_t IDR;

  __IO uint32_t ODR;

  __IO uint32_t BSRR;

  __IO uint32_t BRR;

  __IO uint32_t LCKR;

} GPIO_TypeDef;

typedef struct

{

  __IO uint32_t CR;

  __IO uint32_t CFGR;

  __IO uint32_t CIR;

  __IO uint32_t APB2RSTR;

  __IO uint32_t APB1RSTR;

  __IO uint32_t AHBENR;

  __IO uint32_t APB2ENR;

  __IO uint32_t APB1ENR;

  __IO uint32_t BDCR;

  __IO uint32_t CSR;

} RCC_TypeDef;

 

#define PERIPH_BASE           ((uint32_t)0x40000000)

#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)

#define GPIOD_BASE            (APB2PERIPH_BASE + 0x1400)

#define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)


#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)

#define RCC_BASE              (AHBPERIPH_BASE + 0x1000)

#define RCC                 ((RCC_TypeDef *) RCC_BASE)

 

void Delay(vu32 nCount);

 

int main(void) //main是程序入口

{

      RCC->APB2ENR |= RCC_APB2Periph_GPIOD;

       GPIOD->CRL &=  0xFFF0FFFF;

       GPIOD->CRL |=  0x03030000;

      while (1)

        

             GPIOD->BRR = GPIO_Pin_4;

              Delay(0x2FFFFF);

              GPIOD->BSRR = GPIO_Pin_4;

           Delay(0x2FFFFF);

      }

}

void Delay(vu32 nCount)             //通过不断for循环nCount次,达到延时的目的口

{

  for(; nCount != 0; nCount--);

}

   5.7.2  例程环境搭建

1.    点击桌面UVision4图标,启动软件,如下图。如果是第一次使用的话会打开一个自带的工程文件,我们可以通过工具栏Project->Close Project选项把它关掉。

http://s5/mw690/002aCJOcgy6Ma9F5qBe34&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

http://s8/mw690/002aCJOcgy6Ma9FcI8D57&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

2.    在工具栏Project->New μVision Project新建我们的工程文件,我们将新建的工程文件保存在 “STM32神舟开发板模板工程”(先在电脑硬盘上新建一个“STM32神舟开发板模板工程”,在该文件夹里面新建一个Project文件夹),文件名取为神舟STM32-DEMO(英文DEMO的意思是例子),名字可以随取,点击保存。

http://s14/mw690/002aCJOcgy6Ma9F8l657d&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

http://s12/bmiddle/002aCJOcgy6MaaupVTZcb&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

3.    接下来的窗口是让我们选择公司跟芯片的型号,我们用的是STM32神舟III号的板子,因为我们的STM32神舟IV号用的芯片是ST公司的STM32F107VCT6,有64K SRAM,256K Flash,属于高集成度的芯片。按如下选择即可。

http://s15/mw690/002aCJOcgy6Ma9GPg5gee&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

http://s15/mw690/002aCJOcgy6Ma9GV6kC4e&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" /> 

4.    接下来的窗口问我们是否需要拷贝STM32的启动代码到工程文件中,这份启动代码在M3系列中都是适用的,一般情况下我们都点击是,但我们这里用的是ST的库,库文件里面也自带了这一份启动代码,这里我们点击否。

http://s8/mw690/002aCJOcgy6Ma9GUWHBb7&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

5.    此时我们的工程新建成功,如下图所示。但我们的工程中还没有任何文件,接下来我们需要在我们的工程中添加所需文件。

http://s10/mw690/002aCJOcgy6Ma9GSNnjb9&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

6.可以看到目前工程里只有一个文件:http://s8/mw690/002aCJOcgy6Ma9IRu3tc7&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

7.新建一个main.c文件存放在路径STM32神舟III号开发板第1个例程\Project\main.c下,然后按照以下图标操作过程把main.c文件添加到工程里:

http://s2/bmiddle/002aCJOcgy6Ma9IMtQ5b1&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

http://s13/bmiddle/002aCJOcgy6Ma9J23yk7c&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

这个例程,我们将所有的代码都写到了一个mian.c文件,不涉及到任何库函数,也没有包含任何的头文件,下面我们的截图:

http://s4/mw690/002aCJOcgy6Ma9ITcMX73&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

8.把5.61节的代码直接全盘拷贝到main.c文件里

http://s1/mw690/002aCJOcgy6Ma9J4jFC70&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

9.编译成功,我们可以看到编译后的HEX文件,我们可以直接在光盘中找到这个文件,直接进入Project文件夹,打开即可:

http://s2/mw690/002aCJOcgy6Ma9KtFZve1&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />
http://s10/mw690/002aCJOcgy6Ma9KxVEZ09&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

10该代码可以直接下载到神舟IV号开发板中,按一下复位按键,可以看到LED灯一亮一灭,具体下载方式我们推荐有三种,具体下载设置请参考手册其他章节:

1JLINK V8仿真器下载(我们推荐)

2ULINK2仿真器下载

3)串口下载


11.或者直接打开我们已经编译好的例程“STM32神舟IV号开发板从零开始建立一个模板工程”,在http://blog.sina.com.cn/s/blog_7691b90c0101edpx.html链接中,STM32神舟IV号开发板区域可以找到。

可以看到STM32神舟IV号开发板的LED3灯一亮一灭的闪烁。

   例程软件架构和代码分析(只有一个main.c文件)

   5.7.3  代码详细分析:

http://s12/mw690/002aCJOcgy6Ma9KyCRtbb&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

分析1volatile是什么?怎么用?

答:简单的说,就是不让编译器进行优化,即每次读取或者修改值的时候,都必须重新从内存或者寄存器中读取或者修改,防止从缓存处读取的值是过期了的,所以加了这个volatile可以保证每次读的值绝对是实时的:

一般说来,volatile用在如下的几个地方:

1.中断服务程序中修改的供其它程序检测的变量需要加volatile

2.多任务环境下各任务间共享的标志应该加volatile

3.存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义。

我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到 volatile变量。不懂得volatile的内容将会带来灾难。假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。

 

分析2__I__O__IO是什么?

答:如下:

__I :输入口。既然是输入,那么寄存器的值就随时会外部修改,那就不能进行优化,每次都要重新从寄存器中读取。也不能写,即只读,不然就不是输入而是输出了。

__O :输出口,也不能进行优化,不然你连续两次输出相同值,编译器认为没改变,就忽略了后面那一次输出,假如外部在两次输出中间修改了值,那就影响输出。

__IO:输入输出口,同上。

 

分析3:为什么加下划线?

答:原因是避免命名冲突,一般宏定义都是大写,但因为这里的字母比较少,所以再添加下划线来区分。这样一般都可以避免命名冲突问题,因为很少人这样命名,这样命名的人肯定知道这些是有什么用的。

经常写大工程时,都会发现老是命名冲突,要不是全局变量冲突,要不就是宏定义冲突,所以我们要尽量避免这些问题,不然出问题了都不知道问题在哪里。

 

分析4typedef是什么意思,怎么使用

答:typedefC语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等);在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。

1)  typedef的最简单使用,例如:typedef long byte_4;表示给已知数据类型long起个新名字,叫byte_4

2)  typedef与结构结合使用

例如:typedef struct tagMyStruct   

{   

int iNum;   

long lLength;   

} MyStruct;

这语句实际上完成两个操作

操作1定义一个新的结构类型tagMyStruct, struct 关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为structtagMyStruct合在一起才能表示一个结构类型。

struct tagMyStruct   

{   

int iNum;   

long lLength;   

};

操作2typedef为这个新的结构起了一个名字,叫MyStruct

typedef struct tagMyStruct MyStruct;   

因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。

 

分析5:所以具体的typedef代码解释如下:

1)例:typedef   unsigned  int    uint32_t;

表示使用uint32_t符号表示unsigned  int符号

2)例:typedef  __IO  uint32_t  vu32;

表示使用vu32符号表示typedef  __IO符号

3)例:typedef   unsigned short int   uint16_t;

表示使用uint16_t符号表示unsigned short int符号

 

2.初始化宏定义详细分析分解:

http://s4/mw690/002aCJOcgy6Ma9KYXx953&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

分析1:可以看出规律,GPIO_Pin_0GPIO_Pin_1GPIO_Pin_15总共16define定义每个都是一个16比特(uint16_t)的对象。

每个端口都有16GPIO管脚,比如GPIOAGPIOBGPIOC等,我们用16bit的位来表示,即2个字节,每个bit表示16GPIO管脚中的一个,可以看下面2个寄存器,就是控制GPIO对应的具体管脚是高电平还是低电平的,通过对设置对应位为0或为1,就可以使得管脚的电平为高或低。

每个GPIO_Pin_x占用这16个比特中的1个位,其他剩余的15个位都是0,这16GPIO_Pin_x就被用来表示芯片各个不同端口的16个管脚,比如PA0PA1一直到PA15分别对应GPIO_Pin_0GPIO_Pin_1GPIO_Pin_15,这样定义好之后具体如何使用我们后面还会再说。

#define GPIO_Pin_0       ((uint16_t)0x0001)    0000 0000 0000 0001

#define GPIO_Pin_1       ((uint16_t)0x0002)    0000 0000 0000 0010

#define GPIO_Pin_2       ((uint16_t)0x0004)    0000 0000 0000 0100

#define GPIO_Pin_3       ((uint16_t)0x0008)    0000 0000 0000 1000

#define GPIO_Pin_4       ((uint16_t)0x0010)    0000 0000 0001 0000

#define GPIO_Pin_5       ((uint16_t)0x0020)    0000 0000 0010 0000

#define GPIO_Pin_6       ((uint16_t)0x0040)    0000 0000 0100 0000

#define GPIO_Pin_7       ((uint16_t)0x0080)    0000 0000 1000 0000

#define GPIO_Pin_8       ((uint16_t)0x0100)    0000 0001 0000 0000

#define GPIO_Pin_9       ((uint16_t)0x0200)    0000 0010 0000 0000

#define GPIO_Pin_10      ((uint16_t)0x0400)    0000 0100 0000 0000

#define GPIO_Pin_11      ((uint16_t)0x0800)    0000 1000 0000 0000

#define GPIO_Pin_12      ((uint16_t)0x1000)    0001 0000 0000 0000

#define GPIO_Pin_13      ((uint16_t)0x2000)    0010 0000 0000 0000

#define GPIO_Pin_14      ((uint16_t)0x4000)    0100 0000 0000 0000

#define GPIO_Pin_15      ((uint16_t)0x8000)    1000 0000 0000 0000

#define GPIO_Pin_All     ((uint16_t)0xFFFF)    1111 1111 1111 1111

 

分析2:这里是定义GPIO端口B的一些初始化变量,后面那些具体的地址需要查看芯片参考手册

http://s3/mw690/002aCJOcgy6Ma9KOk38c2&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

http://s13/mw690/002aCJOcgy6Ma9MtcbW8c&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

1)定义总线的基地址(这个需要参考手册)

#define   PERIPH_BASE    ((uint32_t)0x40000000)

2APB2PERIPH_BASEAPB2时钟总线)的地址是在总线基地址上加多0x10000,刚好就是上图的AFIO寄存器地址,具体可以看参考手册

#define   APB2PERIPH_BASE     (PERIPH_BASE + 0x10000)

3)定义GPIO端口D的基地址,该地址是0x40001 1400

#define   GPIOD_BASE      (APB2PERIPH_BASE + 0x1400)

4)定义一个GPIO_TypeDefstruct结构,从GPIO端口F的基地址开始进行覆盖

#define   GPIOD     ((GPIO_TypeDef *)  GPIOD_BASE)

 

分析3:这里是初始化RCC时钟总线的基地址,详细分析与上面同原理,具体的地址需要查看芯片参考手册

http://s3/bmiddle/002aCJOcgy6Ma9McQxQ02&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

http://s14/mw690/002aCJOcgy6Ma9MuQkB3d&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

分析4:设置端口的偏移量,后面我们会详细解释http://s14/mw690/002aCJOcgy6Ma9MtgeN4d&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

3.定义这两个结构体与芯片参考手册中的寄存器进行对应,芯片参考手册中对应的寄存器都是32bit的,所以在这个结构体的各个对象都被定义成uint32_t类型,并且是__IO类型,表示每次操作寄存器都是实时获取数据:

http://s11/mw690/002aCJOcgy6Ma9MDDRgaa&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

4.下面是main函数的剖析,总共来说分为4个步骤,下面一一介绍:

http://s13/mw690/002aCJOcgy6Ma9NZzf6bc&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

步骤1:使能APB2总线的时钟。

GPIO的端口D时钟使能,这个是芯片厂家所规定的操作,我们先按照这样来操作就可以,具体实现方式也是将对应GPIOD寄存器使能,同时,也有RCC,串口接口,CAN接口,485接口等时钟的使能寄存器,使用前都需要先对时钟总线使能的。

使能操作完毕,就相当于我们对要使用的这个接口功能进行使能和激活,每个接口在使用前都必须要求使能和激活,只有激活后才可以使用。

步骤2:配置GPIO端口的状态。

输入还是输出,速度多少,和大家可以参考对应的芯片寄存器手册,可以看到我们将PB2设置成‘00:通用推挽输出模式’并且速度是‘11:输出模式,最大速度50MHz

具体可以参考GPIOD_CRL寄存器,我们将这个寄存器设置为了0x0300 0000

步骤3:进入while死循环

可以使得我们的点灯程序一直不会退出,达到重复一亮一灭的功能。

步骤4GPIO输入和输出使得灯亮灭

GPIOD_BSRR对应位设置1,可以使得对应管脚的ODR位为1GPIO_BRR的对应位设置1,可以使得对应管脚的ODR位为0

       那么ODR位是什么呢?这个就是端口输出数据寄存器GPIOB_ODR,实际上这个寄存器里的对应位的变化才是真正GPIO管脚高低电平的变化;而寄存器GPIOB_BSRR和寄存器GPIOB_BRR则可以间接影响到它。

       当然上面程序,我们也可以改成直接操作GPIOF_ODR的代码,这个留个大家来做练习吧。

http://s5/mw690/002aCJOcgy6Ma9OkP1W24&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

http://s14/mw690/002aCJOcgy6Ma9O90OV8d&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

   5.7.4  程代码详细说明

 代码的定义和声明如何与芯片内部资源挂钩

 C语言程序代码如何真正访问芯片内部寄存器的呢?大家看下面这些定义:

http://s11/mw690/002aCJOcgy6Ma9ObGMG9a&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

通过这几个define可以算出来一下地址:

GPIOD= 0x4000 0000 + 0x1 0000 + 0x1400 = 0x4001 1400刚好与PortD在内存中的位置对应上

RCC = 0x4000 0000 + 0x2 0000 + 0x1000 = 0x4002 1000 刚好也与RCC在内存中的位置对应上

http://s9/bmiddle/002aCJOcgy6Ma9Ous8U28&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

    http://s7/bmiddle/002aCJOcgy6Ma9PRRoGb6&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" /> 

更多内存映射可以找STM32F10XXX的数据手册资料,如下图,可以详细的进行了解了http://s10/mw690/002aCJOcgy6Ma9Q9v7389&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

 http://s7/mw690/002aCJOcgy6Ma9QbUYSe6&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

通过以上这个存储器映像图,我们就可以通过代码与存储器地址关联起来了。

   5.7.5  码如何映射到映射到芯片内部的寄存器

 这也是从库函数中摘抄出来的一种实现方式,我们通过struct结构可以完成对GPIO,RCC等外设模块中各个寄存器的管理,比如,一个GPIO模块中,有很多个寄存器,我们可以用C语言中的struct来对应这些寄存器。

    在芯片中,一个寄存器连着一个寄存器,每个寄存器都是32位的(4个字节);我们在struct结构中的成员每个也都是32位的,一个连着一个,刚好一一对应,大家可以看下图:

http://s12/mw690/002aCJOcgy6Ma9Q9OTF9b&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

http://s14/bmiddle/002aCJOcgy6Ma9REq3Xcd&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

可以看到上图,每个寄存器都是32位的,左边是而且顺序刚好分别对应,结构体是会分配内存的,这样这些C语言中的struct结构体中定义的成员会对应映射到对应的寄存器上,那么我们就可以通过操纵程序中的该结构体的对应成员,就相当于操作的是对应的寄存器,这个是C语言和单片机软硬件对应上的又一大关键点,请不熟悉的读者好好理解一下,如实在不理解,可以致电STM32神舟系列开发板的官方工程师。


   5.7.6  main函数寄存器级分析(重点)

1.LED灯为什么会一亮一灭呢?

http://s3/mw690/002aCJOcgy6Ma9RPhmOe2&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

我们看一下参考手册中的GPIOx_BRRGPIOx_BSRR两个寄存器:

http://s5/mw690/002aCJOcgy6Ma9RR1n6a4&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

http://s9/mw690/002aCJOcgy6Ma9S7hTW58&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

http://s1/mw690/002aCJOcgy6Ma9SenFSd0&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

分析1:从以上两个寄存器里的内容可以知道到:

l         ODRy = 1就会输出高电平,如果是高电平,我们查看了原理图,这个LED灯灭,可以通过操作 GPIOx_BSRR寄存器的对应位来改变

l         ODRy = 0就会输出低电平,如果是低电平,我们查看了原理图,这个LED灯亮,可以通过操作 GPIOx_BRR寄存器的对应位来改变

分析2:进一步分析,我们如何通过代码来改变这个ODRy呢?大家请看下面:

l         代码GPIOB->BRR = GPIO_Pin_2;可以使得GPIOx_BRR寄存器的BR2位为1这样就是的GPIO端口B的对应ODR2=0,即PB2管脚输出低电平,使得LED2亮。

l         代码GPIOB->BSRR = GPIO_Pin_2;可以使得GPIOx_BSRR寄存器的BS2位为1这样就是的GPIO端口B的对应ODR2=1,即PB2管脚输出高电平,使得LED2

 

2.要使用PB2管脚需要做哪些初始化的工作呢?

http://s1/mw690/002aCJOcgy6Ma9ToTYIa0&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

   分析1:使能APB2总线上的GPIO端口F的时钟,我们可以看下系统图,可以看到GPIOF是属于APB2总线管理的,那么如何初始化这个是ST公司要求的,而不是我们STM32神舟系列开发板官方规定的,一切都是STM32芯片厂家ST公司制定的。

http://s10/mw690/002aCJOcgy6Ma9TDVeV19&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

    分析2:代码RCC->APB2ENR |= RCC_APB2Periph_GPIOB;是使能GPIOB的时钟,我们找到寄存器RCC_APB2ENR,仔细看看是什么操作的

http://s15/mw690/002aCJOcgy6Ma9TLzUG9e&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

点击进入文档第61页可以看到6.3.7节,可以看到GPIOB端口的时钟设置选项:

http://s16/mw690/002aCJOcgy6Ma9U5d1R5f&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

     http://s7/mw690/002aCJOcgy6Ma9U1puCa6&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

我们通过代码RCC->APB2ENR |= RCC_APB2Periph_GPIOB;来对RCC_APB2ENR寄存器的位3进行操作置位,那么RCC_APB2Periph_GPIOF的值请见:

代码:#define RCC_APB2Periph_GPIOF             ((uint32_t)0x00000080)

从这句代码可以知道0x000000808化成二进制是1000,刚好是对RCC->APB2ENR寄存器也就是RCC_APB2ENR寄存器的第7位置位,使得IOPF EN1,使得IO端口F时钟使能。

 

分析3:配置GPIO端口F的工作模式,我们这里可以看到它配置改变了CRL这个寄存器。

http://s1/mw690/002aCJOcgy6Ma9V3EfC40&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

我们找到这个CRL寄存器完整的名称叫GPIOx_CRL寄存器的内容:

http://s2/mw690/002aCJOcgy6Ma9Vrn9vc1&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

http://s16/mw690/002aCJOcgy6Ma9VAQbB6f&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

我们开始分析一下代码:

l         GPIOB->CRL &=  0xFFF0FFFF;

GPIOB->CRL &=  0xFFF0FFFF = 1111 1111 1111 0000 1111 1111 1111 1111

可以看到将CRL寄存器的第1617、18、19位清0,其他位默认值不变,看上表可以知道,这4位刚好是管脚PB2的配置寄存器http://s5/mw690/002aCJOcgy6Ma9VD2yE24&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />


l         GPIOB->CRL |=  0x00030000

GPIOB->CRL |=  0x00030000= 0000 0000 0000 0011 0000 0000 0000 0000

可以看到将CRL寄存器的第191817、16 分别置成0011,其他位的GPIO端口值都清0,意思就是只配置PB2这个管脚,其他PB口的管脚配置寄存器都全部变成0,这样MODE4=11CNF4=00查表可以知道

http://s13/mw690/002aCJOcgy6Maauun0g9c&690分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" TITLE="5.7. 分析一个最简单的例程-STM32(初学宝典)神舟IV号开发板" />

这样配置后PD4被配置成输出模式,输出速度为50MHz,状态是通用推挽输出模式;因为我们这个例程中需要点LED灯,这是一种输出模式。


5.7.7 函数与我们这个例程之间的关系

库函数同我们这个例程的原理是一样,后续的例程,都是源自库函数的,阅读原理一样,只是我们将一些相关功能比较密切的代码封装到一起,变成一个完整的函数。

后续该神舟文档还会升级,库函数分析版本请见下个版本的书籍。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有