欢迎光临散文网 会员登陆 & 注册

如果不会寄存器开发而陷入瓶颈, 那么本文将会有较大帮助

2022-05-13 14:40 作者:大方老师单片机课堂  | 我要投稿

如果不会寄存器开发而陷入瓶,那么本文将会有较大帮助


///插播一条:我自己在今年年初录制了一套还比较系统的入门单片机教程,想要的同学找我拿就行了免費的,私信我就可以~点我头像黑色字体加我地球呺也能领取哦。最近比较闲,带做毕设,带学生参加省级或以上比///


0.绪论

对于经过系统培训的开发.单片机SoC的驱动开,不管是使用各种库还是直接上寄存,都不成问.

可是很多非专业但是需要干嵌入式或单片机工程的,比如机,车辆工,,或是其他的一些专.有时候这些学生需要搞比,做项,不可避免的要用到一些单片(由于种种原,现在几乎都会首STM32).但是缺乏系统训练的学生往往无法看懂寄存器版本的例,或是别人的开源项.自己写的话更是不知道如何开.或者编译完成后总是出现莫名的问.这给大家带来了极大的困.

同时互联网上大部分教程都是转载来转载,各种差异和版本都.很多人即使看过了也是一头雾.所以应一个同学要,大概写一下寄存器的一些操.

本教程将会使"学生用户体量庞"STM32F1xx系列的单片机作为例.但是不必担.所有的寄存器操作都是共通.并不会有本质的差.希望你能快速阅读完并且理解,然后查阅自己需要的资.不管是什么东西,这种方式都是通用.

嵌入式软件开发具有一个比较庞大的知识体.限于作者的时间和水,本文不可能讲太多东西.但是如果你只是因为不会寄存器开发而陷入瓶,那么本文将会有较大帮助(毕竟这个是写文章的目).

理解本文需要一些基,大概包: c/c++,一些数,计算机基础知.大多数理工类专业都会有相关的课程.如果确实存疑可以即时搜.

让我们先记住开发的正确方: 摘抄借,修改糅,以实现功能为目,以别人的代码为基.

只要不涉及什么知识产权的,这样做是完全没有问题.毕竟不需要把很多基础性的东西写来写.所以请大胆的找开源项,尽可能在基础上,而不是从零开.不废话,开始正.

比如十进制下0.1就是二进制的无限不循环小.上面那个例子也.

著名 IEEE754 float浮点数标准导致bug:在很多语言, 0.1+0.20.3


就是因0.1是二进制无限循环小数的原.但是存储器位宽不能是无穷.所以产生舍入误.


在浏览器中按F12进入开发者模,JavaScript下的浮点数精bug

所以在很多项目,为了实现当两个值相等时触发什么函,往往不会直接写相,而是两者的差值小于多少时即生效

floata,b;........if(a==b){

//这种写法不建议}........if(abs(a-b)0.00001){

//一般这么写}

二进制与/十六进制的转换

刚才那个方法就可以直接.还有其他算法这里不.先让我贴一个表:

BIN

DEC

HEX

0000

0

0

0001

1

1

0010

2

2

0011

3

3

0100

4

4

0101

5

5

0110

6

6

0111

7

7

1000

8

8

1001

9

9

1010

10

A

1011

11

B

1100

12

C

1101

13

D

1110

14

E

1111

15

F

BIN是二进, DEC是十进, HEX是十六进.都是英语简.

十六进制和二进制可以直接换.方法是每四位二进制看作一个十六进制

比如

0110 1101 1011 1001

6 D B 9

转换表背过的话读代码快.因为一般情况,为了让一行代码看的不至于太,人们会用十六进制代表二进.尤其是对于拥32cortex-M3内核STM32F1系列单片,一次写一个三十二位数属实太冗.

c/c++,默认写的数字都是十进.二进制应该0b,0b00101100,而十六进制0x,0x3C.

//一般这么写GPIOB->CRL&=0x00440000;//这么写就不太美观了GPIOB->CRL&=0b00000000010001000000000000000000;

数学差不多.开始正.

2. c/c++语言基础

a+b; //加法

a-b; //减法

a*b; //乘法

a/b; //,所有计算需要注意整()和浮点()的运算区.如有疑问自行搜索

a%b; //.就是小学学的余. 14÷4=3 ... 2 14mod4=2, 14/4=3. float(14)/4=3.5f

a</.a看为二进制,整个向左移b. 00000110< 00011000.当溢出的时候会发生什么?

a>>b; //.和上面一样的功.

a&b; //按位求(AND), 11010010

// & 01010011

// ------------

// 01010010

a|b; //按位取(OR)

~a; //按位取(NOT)

!a; //逻辑(注意和上面的区)

c+=a; //相当 c=c+a;其他算符同.

重点看左右移和位操(,,).

3.我们配寄存(register)到底是在配置什么

,你的最终目的都是使用单片机GPIO(general pin input & output)/输出一个高电平还是低电.不管是诸I2C, SPI的通,还是按键读,亮灯报,说到底都是高低电平的控制或探.

ADC输入的是模(Analog),但是会被转化为数(Digital),一样是高低电.这里暂且不谈
,,或者一些功能的使能及配置一样是通过寄存器.原理相.毕竟芯片集成电路也是电,而且是数字电.暂时不深.

,为了让某一Pin输出电,或者使能一个通,我们可以01.

但是代码终归是代,不是魔.为了使需求生,单片机将每一个需要控制的,赋予一个地址.在电路层面上实现相关的功能绑.用户只需通过给这个地址去写一个,就相当于控制了需要控制的东西.易于计,我们32位处理器最大可以寻4GB的内存空.

:并不是每一个地址都是有真实物理地址对应.换而言,一个地址可能指向的是真实的内,也可能并不是真实存在的内.不过访问这个地址相当于控制了被控量一.此时该地址可以看作控制量的一个(handler).
"使能"的意思是enable.反义词"禁用(disable)".计算机相关的词汇总是这么的看不懂字面意.看英语就明白.
内存是存储数据的地.任何电子信息数据都可以看01. 32位机的最小存储单元DWORD(, Double Word),32比特. 32位机的内存单元可以看作一个个存32比特位的小仓.为了找到所需要的数,需要给这些仓库编.这个所谓的编号就是地址(Address).存储的值是地址值的变量叫做(pointer).比如一个仓库放着一个记录某个货物的多个存放仓库的编,那么这个内存里的数据就是一个指.计算机无法分辨哪写是指,哪些是数.这需要人去完.

库函数是寄存器的封装.不管STHAL硬件抽象层,还是标准STL,都是封装而.本质上是宏定义替换和一些函.宏定义在编(compile)阶段完.不占用宝贵的存储空.函数本身会被编译为代,所以会占用空.尽量避免在库中使用函.当然用户代码肯定无所.

为什么要用库函数?比如

GPIO_InitTypeDef GPIO_Initure;

GPIO_Initure.Pin=GPIO_PIN_13;

GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;

HAL_GPIO_Init(GPIOB,&GPIO_Initure);

HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);

这一段代,不加注释你也知道他的意思是初始PB13为推挽输.

但是如果写成了寄存器版本的你可能就看不懂.

GPIOB->CRH&=0XFF0FFFFF;

GPIOB->CRH|=0X00300000;

GPIOB->ODR|=1<

现在会简要介绍寄存器的配置方.你能搜到这个文章肯定是因为你找见的开源代码使用的是寄存器版. 正常人的,能找见库函数绝对是不会用寄存器版本的代码.

寄存器版本的代码实际上也是封,比如上述代码实际上也可以直接写成

(uint32_t *)(((((uint32_t)0x40000000) + 0x10000) + 0x0C00)+0x01)&=0XFF0FFFFF;

(uint32_t *)(((((uint32_t)0x40000000) + 0x10000) + 0x0C00)+0x01)|=0X00300000;

(uint32_t *)(((((uint32_t)0x40000000) + 0x10000) + 0x0C00)+0x04)|=1<

//这一段代码可能不对

上面就是直接通过指针操作寄存器的方.寄存器宏定义和结构体相当,先找GPIOB的开头地,然后加上结构体的附加地址偏(去操作某一个功,CRH寄存).最后给这个地址写入一串二进制值去控制相应的东西.

说到底还是去给某些比特上写高低电.

类似GPIOB->CRH,实际上是定义的一个结构.

typedefstruct{

__IOuint32_tCRL;

__IOuint32_tCRH;

__IOuint32_tIDR;

__IOuint32_tODR;

__IOuint32_tBSRR;

__IOuint32_tBRR;

__IOuint32_tLCKR;}GPIO_TypeDef;

通过结构体的位关系以及各种宏定义替,可以实现地址的编译时自动确.

寄存器的封装好在对于实际内存位置不同的芯,移植只需更改寄存器宏定义映射即.而且已经很容易读.

4.常见的寄存器配置初始(使STM32F1xx系列举例)

我们写驱,最常用的就GPIO的配置.我们就举一些例.

RCC寄存器

RCC寄存器一般用来配RCC时钟相关的代.比如我要使PA0,我必须开PA的时钟通道才.时钟相当于处理器的心,没有心跳当然是死.但是没理由的开启时钟会带来额外EMI干扰和整机功.原理暂且不,实际危害就是干扰大,耗电多.尤其针对于一些对功耗要求严格的场,例如手环手,应该尽可能在待机时关闭能关闭的所有时.用来省(比如有的智能手表升级系统后待机变久,可能就是这个原.我们说的(firmware)升级实际上指的就是把新写的代码下载到了老设备).

举个例, RCC时钟的配置如下

RCC->APB2ENR|=1</使PORTC时钟

RCC->APB2ENR|=1</使PORTB时钟

RCC->APB2ENR|=1</使PORTA时钟

具体使能哪一个位是控制,需要查(除非你背过).

如果是需要快速出项,不管其他,可以省,直接使能所有时.但是不建议这么.

如果不会寄存器开发而陷入瓶颈, 那么本文将会有较大帮助的评论 (共 条)

分享到微博请遵守国家法律