故事很多,我打算用一辈子来跟你讲,你准备好了吗?(狗头

没必要一次看完,多看几遍消化消化。

所有操作,最终目的都是操作寄存器

stm32编程实质上是修改寄存器的32位的具体的值

单片机

sfr P0=0x80; //关键字sfr 声明地址和名称的映射

P0=0x00; //将0x00赋值给P0口的8位(51单片机一组IO为8位)

STM32

GPIOA->ODR=0x00000000 //为GPIOA的ODR寄存器地址赋值0x00000000

什么是寄存器?

寄存器是中央处理器内的组成部分,它可以从其他寄存器,或者内存中获取指令,地址,数据然后给CPU用,有时候 由于内存太慢,内存和寄存器之间要加一个叫Cache的东西,为了弥补内存的不足,内存-》Cache-》寄存器-》CPU。

指令、地址寄存器与数据寄存器都类似,里边存放的都是0和1,毕竟单片机也只认识机器码,机器码都是0或1,只是特别的规定下,数据寄存器里面存放的0和1表示数据,指令寄存器里存放的表示指令。

既然有不同的寄存器,那CPU怎么去区分这些寄存器?

---通过地址,STM32给不同的寄存器分配了不同的地址。

怎么找到某个寄存器的地址?---查看数据手册

补充:32位cpu代表了什么,stm32也是32位

代表的是数据总线32位,也就是说cpu一次可以处理32位数据,而上面我们说了,cpu要从寄存器取数据,所以它寄存器也是32位的,所以32位代表什么?代表它的寄存器是32位的。

敲黑板:stm32的寄存器是32位的,都是存储8位16进制的数或者32位2进制的01数字。

什么是地址映射?

所谓地址映射,就是将芯片上的寄存器 甚至I/O等资源与地址建立一一对应的关系。

如果某地址对应着某寄存器,我们就可以运用C语言的指针来寻址并修改这个地址上的内容,从而实现修改该寄存器的内容。

所以我们能操作IO资源(如引脚)的根本原因是,这些引脚由于地址映射与地址建立了对应关系,而 部分地址 又和 寄存器建立了对应关系,所以,我们说操作某些资源就是操作某些寄存器应该不难理解了

IO资源---地址---寄存器

正是因为头文件中有了对于各种 寄存器 和 I/O端口 的地址映射,我们才可以在51单片机程序中方便地使用P2^0 =0xFF; TMOD =0xFF等赋值句子对寄存器进行配置,从而控制单片机。

我们可以看出地址才最重要的纽带,那么我们怎么去访问地址,能访问多少?怎么抽象出地址这一概念的?是不是得有一些东西支撑?这个东西就是地址总线。cpu通过地址总线访问地址,我们操作地址当然也是通过地址总线啦,因为我们操作着CPU嘛(滑稽),假如一个芯片有32根地址总线,每根线有两种状态(0/1)所以32根能表示2^32个状态,一个状态代表一个地址,所以这一共有4G的地址能表示(访问)。

2^10 = 1024 = 1Kb

2^30 = 1024x1024x1024= 1024*(1024*1kb) = 1024Mb = 1G

2^32 = 1G x 4= 4G

APB2总线就是指特定的一段地址,如:0x4001 0000—XXXX

然后官方书上有段话,大致意思:你能理解这句话的深刻含义吗?

程序存储器、数据存储器、寄存器和输入输出端口 被 组织在同一个4GB的线性地址空间内。

说到映射大家可能就会想到函数映射,脑海里会有一个画面:左边一个集合中的某个元素“射”出一条带箭头的直线指向右边的集合的某个元素。如果你高数或离散数学再好一点儿的话会想到单射、双射、满射、恒等映射(你要是这个都想到了,那你牛B!)……其实外围设备的内存映射原理是一样的,只不过左边的集体变成了CPU,右边的集合变成了外围设备,那条带箭头的线就是连接CPU和外设地址引脚的地址总线。

学习stm32最困难的地方,就是理解功能对应的寄存器的逻辑关系,甚至一个简单的功能往往是多个寄存器的调用的结果,理解寄存器之间的关系,学会查看寄存器的值。

以GPIOA为例说明STM32寄存器和名称的映射

GPIOA下的某个寄存器,挂载在GPIOA下,地址为GPIOA基地址+偏移量

GPIOA挂载在APB2总线,地址为APB2总线基地址+GPIOA偏移量

ABP2挂载加外设基地址,地址为外设基地址+ABP2偏移量

源码中可以找到:

//外部总线基地址 8位16进行不刚好是32位2进制吗?---地址总线的作用别忘了

#define PERIPH_BASE ((uint32_t)0x40000000)

//APB2基地址=外部总线基地址+偏移量

#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)

//GPIOA基地址=APB2基地址+偏移量

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)

//GPIOA将地址顺序分配给7个32位寄存器(结构体分配)

#define GPIOA ((GPIO_TypeDef*)GPIOA_BASE)

/将寄存器地址映射到7个32位寄存器,分别控制

typedef struct

{

IO unit32_t CRL;

IO unit32_t CRH;

IO unit32_t ODR;

IO unit32_t IDR;

IO unit32_t BSRR;

IO unit32_t BRR;

IO unit32_t LCKR;

}GPIO_TypeDef;

关于寄存器的都说了,怎么查寄存器的地址呢?

上面说了利用参考手册找,参考手册网盘链接在文末

假如你想读取PB3引脚的电平

第一步 找到GPIOB的基地址:(以后找就来这)

第二步找到端口输入寄存器的地址偏移(结论是0x4001 0C00+8 = 0x4001 0C08),地址偏移上面写着 8,PB3的数据位于从右往左数第4个 即IDR3

这张图片包含了很多信息,首先这两行表示的是一个寄存器的内部结构,上面一行告诉我们保留,就是放着不用,始终读为0,而下面就是0~15个引脚位置,最下面的r的意思是只读,不能写

总结:PB3的输入数据位于0x4001 0C08这个地址上,这个地址上所存放数据的右起第4个位就是PB3引脚 对应 的高低电平

我们可以简单粗暴地直接访问这个地址:

unsigned int *pGPIOB_IDR = (unsigned int *)0x40010C08;

unsigned char PB3 = *pGPIOB_IDR & 0x8;//取出从右往左数的第4位

直接访问的操作并不好用,每操作一个寄存器就必须去查看数据手册,然后找找这个寄存器的地址。

意法半导体公司为了方便大家使用,就把这些寄存器都起了一目了然的名字,把寄存器与地址映射关系放在他们提供的头文件里。这个文件就是stm32f10x.h。(我们上面地址映射那段讲到的知识又用到了,现在是读这段话是不是觉得理所当然,是不是知道自己再做什么?而不是无脑比着其他代码写)

直接操作寄存器来点亮LED。

我的板子对应的LED是PB8。首先要配置时钟使能。

为什么配置时钟?为了省电,默认的时钟都是关闭的。配置STM32的任何资源前,都必须首先使能时钟。配置哪个时钟?

时钟的信息在参考手册里边,参考手册十分巨大,不用通读,就像一个字典,需要什么查什么。

时钟控制名字叫做RCC,属于AHB总线。GPIOB属于APB2。

下图系统结构可以看到时钟的从属关系,此图位于手册P25页,十分重要。可以看出AHB总线包含RCC时钟控制,GPIO是属于APB2的。

我们已经知道,GPIO端口B的地址从0x4001 0C00开始。接下来只寻找时钟使能寄存器的地址:

复位和时钟控制RCC的地址从0x4002 1000开始。

可以在6.3.7小节找到APB2外设时钟使能寄存器(RCC_APB2ENR),偏移地址是0x18,所以APB2的地址就是0x4002 1018。

看手册RCC_APB2ENR,位3是IOPBEN---IO端口B时钟使能,就是我们想要的。把RCC_APB2ENR的位3赋值为1,就是开启GPIOB时钟。配置为通用输出

既然叫做IO,那么肯定就是可以输入,可以输出,到底是输入还是输出呢?

控制LED需要输出高电平或是低电平,所以需要配置为输出。

一个STM32的IO需要4个位来配置,所以一个32位的寄存器最大只能配置8个IO。STM32中,用端口配置低寄存器(GPIOx_CRL)来配置引脚Px0-Px7, 用端口配置高寄存器(GPIOx_CRH)来配置引脚Px8-Px15。

配置引脚PB8,使用的寄存器是GPIOB_CRH。下面我们来寻找这个寄存器的地址。

因为是PB8所以要配置对应的:CNF8和MODE8

关于此寄存器的说明位于8.2.2小节。先看标题GPIOx,表示PA,PB,PC直到PE都能用。

偏移地址是0x04,意思是在基地址的基础上再加0x04,所以,对于GPIOB来说就是0x4001 0c04。如果配置PB0-PB7,那么需要的寄存器是低位的寄存器GPIOB_CRL,它的地址是0x4001 0c00。我们需要配置的寄存器是GPIOB_CRH。

找到需要操作的寄存器后,把它配置为通用输出。

复位值是0x4444 4444,并不是0x0000 0000。所谓的复位值,就是指如果没有操作这个寄存器时,寄存器存放的默认值。复位值按位拆分0x4 = 0b0100,0x表示16进制,0b表示二进制,也就是默认CNF 01,MODE 00,是浮空输入。

我们需要的是输出高低电平,所以要设置为输出。输出模式又有好几种输出:

推挽输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。

开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。

开漏是需要外接上拉电阻才可以输出高电平的,这里并不适合。所以需要设置为推挽输出。

功能是否是复用呢?复用的意思是有别的功能在这个脚上,比如USB,CAN,串口等,所以这些个脚就可能有多个功能。暂时讲多了反而会迷惑,等用到了这些功能再讲解,我直接告诉大家,PB8没有复用。

所以配置为输出模式,通用推挽输出。速度暂时不关注,随便填写一个50MHz吧,其它速度当然也可以。所以设置GPIOB_CRH的MODE8与CNF8为0b0011,即0x3。此寄存器中其它的位暂时不做修改,使用默认值,也就是GPIOB_CRH设置为:0x4444 4443。点亮LED需要输出低电平

在单片机的编程中,要想做某件事,必须寻找相应的寄存器。

在8.2.4小节,可以找到端口输出数据寄存器(GPIOx_ODR),就是我们需要的。我们需要输出0。但是中文手册有一个小小的BUG,0x0C写成了0Ch,可以参考英文原版。得知地址的偏移是0x0C,所以这个数据寄存器的地址就是0x4001 0C0C,把第8位写为0就行。默认就是0,但是也得学一下怎么写,万一是高电平点亮呢。

使用直接赋值的方式写寄存器的地址

在搞清楚我们要用的几个寄存器的地址,以及寄存器中需要装填的数值以后,现在用一个简单粗暴的方法来操作这些寄存器——直接操作。(注意,这段代码不是实用的代码,只是为了写出一个最简单的LED,有些部分是不可取的。)将main函数修改为:

int main(void)

{

unsigned int *pRCC_APB2ENR = (unsigned int *)0x40021018;

unsigned int *pGPIOB_CRH = (unsigned int *)0x40010c04;

unsigned int *pGPIOB_ODR = (unsigned int *)0x40010c0c;

*pRCC_APB2ENR = 0x00000008;

*pGPIOB_CRH = 0x44444443;

*pGPIOB_ODR = 0x00000000;

return 0;

}

C语言总是从main函数开始执行。

定义几个指针,指向刚刚看到的地址。对于编译器来说,它并不知道0x40021018代表的是数据还是指针,所以用(unsigned int *)作强制的类型转换,告诉编译器0x40021018是个指针。指针可以理解为地址。操作指针,把这些地址存放的值修改。

我们写了一段另类的代码,直接操作寄存器的地址,就是想得到这么一个结论:不论代码怎么写,不论是寄存器,库函数,还是其他的操作系统,要在STM32F103这个单片机点亮LED灯,肯定需要把时钟和GPIO这几个相关的特殊地址,进行赋值或修改数值的操作。有点像打篮球,不论进攻时有怎样花哨的运球与传切配合,最后都要完成把球放入篮筐的动作,才能得分。

参考手册第10版:

crh寄存器_STM32 学习笔记(寄存器)---2相关推荐

  1. crh寄存器_牛人的STM32学习笔记(寄存器版本).doc

    牛人的STM32学习笔记(寄存器版本) 一.GPIO口的配置 STM32的DGPIO口最多可以有7组(GPIOa~GPIOg),而每一组GPIO口均有16个双向IO组成.并且没个IO口均可配置成8种模 ...

  2. 嵌入式学习笔记——寄存器实现控制LED小灯

    文章目录 前言 GPIO通用输出模式 初始化LED小灯的GPIO 原理图 初始化代码 初始化的效果 功能函数封装 直接分开宏定义两个 使用条件运算符 封装函数实现简单的功能 KEIL MDK一些技巧 ...

  3. 标记寄存器---汇编学习笔记

    标记寄存器 CPU内部的寄存器中,有一种特殊的寄存器(对于不同的处理机,个数和结构都可能不同)具有以下3种作用. (1)用来存储相关指令的某些执行结果. (2)用来为CPU执行相关指令提供行为依据. ...

  4. UVM学习笔记—寄存器模型的搭建及使用

    目录 0.前言 1.DUT中的寄存器是什么? 2.为什么需要寄存器模型? 3.如何搭建寄存器模型 3.0基本概念 3.1搭建一个reg(将reg filed加入到reg) 3.2搭建一个reg blo ...

  5. can总线配置读入是什么意思_STM32学习笔记—CAN总线收发数据常见问题分析

    CAN,Controller Area Network(控制器局域网络),在汽车电子.工业控制领域的应用比较多,通常用于局域组网. 这是第9篇学习分享文章,<STM32学习笔记>之CAN总 ...

  6. 32f407tim4时钟源频率_STM32学习笔记04—SysTick时钟

    4.1 SysTick概述 Cortex-M3内核的处理器,内部包含了一个SysTick定时器,SysTick是一个24位的倒计数定时器,当计数到0时,将从ReLoad寄存器中自动重装载定时初值,开始 ...

  7. stm32读操作失败_STM32学习笔记—通信容易出错的情况

    I²C:全称为Inter-Integrated Circuit(内部集成电路),是一种串行通讯总线,常用于嵌入式电子产品中. 这是第4篇分享,<STM32学习笔记>之I2C通信容易出错的情 ...

  8. input内容右对齐_STM32学习笔记—DAC基础内容及常见问题

    DAC,Digital-to-Analog Converter(数模转换器),DA转换和AD转换有着同样重要的作用,在许多场合都能看到DAC的应用. 今天是第8篇分享,<STM32学习笔记> ...

  9. dsp28335 sci中用fifo完成485收发通信_STM32学习笔记—CAN总线收发数据常见问题分析

    CAN,Controller Area Network(控制器局域网络),在汽车电子.工业控制领域的应用比较多,通常用于局域组网. 这是第9篇学习分享文章,<STM32学习笔记>之CAN总 ...

最新文章

  1. 从numpy开启Python数据科学之旅
  2. 注册和登陆与数据库的链接
  3. C# ManualResetEvent
  4. 看看样条插值区间查找函数写的多细腻
  5. 【linux命令】Centos下如何匹配内容在哪个文件中
  6. ASP.NET MVC 4 内容映射
  7. php文件上传sha1,PHP中sha1_file与md5_file哪个更快?
  8. 扩充你的工具箱 - 大行文件的处理
  9. [转]Fedora Core Linux 9 中安装VMware Tools-6.5.0
  10. 有什么可以测试耳机性能的软件吗,测试耳机性能好坏的简单方法
  11. 联想本装系统stop:0X000007B错误[转]
  12. 正则表达式(一)字符串匹配
  13. 什么样的代码是好代码_什么使好代码3 7
  14. python(decorator)
  15. html 5新增技术,HTML5新增元素,标签总结
  16. 三、T100应付管理之采购应付-入库应付管理篇
  17. NoteExpress中PDF批量自定义重命名
  18. 《软件工程与计算(卷二)》-Chapter9-10-软件体系结构
  19. 【GD32】GD32读取ADC数据
  20. java-php-python-ssm漠河旅游官网计算机毕业设计

热门文章

  1. android 地图方向北方,android 地图
  2. python和接码平台对接_python写接码api
  3. TensorBoard一幅图中显示多条曲线
  4. 大型仪器一般都是用计算机,问题:大型仪器一般都使用计算机进行控制,对该计算机除了在性能上能满足要求,在使用方面原则上有何要求。...
  5. UE5学习笔记——前言
  6. IOS cocoapods镜像源更新
  7. java jcr_java – 通过JCR实现基于标签的搜索系统的最佳方式,如Modeshape
  8. Groovy(Java笨狗)系列--datatypes
  9. html动态下拉列表,jQuery实现动态显示select下拉列表数据的方法
  10. 微信小程序-好友列表右侧字母导航功能