如果不会寄存器开发而陷入瓶颈,那么本文将会有较大帮助
///插播一条:我自己在今年年初录制了一套还比较系统的入门单片机教程,想要的同学找我拿就行了免費的,私信我就可以哦~点我头像黑色字体加我地球呺也能领取哦。最近比较闲,带做毕设,带学生参加省级或以上比赛///
0.绪论
对于经过系统培训的开发者.单片机或SoC的驱动开发,不管是使用各种库还是直接上寄存器,都不成问题.
可是很多非专业但是需要干嵌入式或单片机工程的人,比如机械,车辆工程,物理,或是其他的一些专业.有时候这些学生需要搞比赛,做项目,不可避免的要用到一些单片机(由于种种原因,现在几乎都会首选STM32).但是缺乏系统训练的学生往往无法看懂寄存器版本的例程,或是别人的开源项目.自己写的话更是不知道如何开始.或者编译完成后总是出现莫名的问题.这给大家带来了极大的困难.
同时互联网上大部分教程都是转载来转载去,各种差异和版本都有.很多人即使看过了也是一头雾水.所以应一个同学要求,大概写一下寄存器的一些操作.
本教程将会使用"学生用户体量庞大"的STM32F1xx系列的单片机作为例子.但是不必担心.所有的寄存器操作都是共通的.并不会有本质的差异.希望你能快速阅读完并且理解完,然后查阅自己需要的资料.不管是什么东西,这种方式都是通用的.
嵌入式软件开发具有一个比较庞大的知识体系.限于作者的时间和水平,本文不可能讲太多东西.但是如果你只是因为不会寄存器开发而陷入瓶颈,那么本文将会有较大帮助(毕竟这个是写文章的目的).
理解本文需要一些基础,大概包括:c/c++,一些数学,计算机基础知识.大多数理工类专业都会有相关的课程的.如果确实存疑可以即时搜索.
让我们先记住开发的正确方式:摘抄借鉴,修改糅合,以实现功能为目的,以别人的代码为基础.
只要不涉及什么知识产权的话,这样做是完全没有问题的.毕竟不需要把很多基础性的东西写来写去.所以请大胆的找开源项目,尽可能在基础上改,而不是从零开发.不废话了,开始正事.
比如十进制下的0.1就是二进制的无限不循环小数.上面那个例子也是.
著名的IEEEfloat浮点数标准导致的bug:在很多语言中,0.1+0.2≠0.3
就是因为0.1是二进制无限循环小数的原因.但是存储器位宽不能是无穷的.所以产生舍入误差.
在浏览器中按下F12进入开发者模式,尝试JavaScript下的浮点数精度bug
所以在很多项目中,为了实现当两个值相等时触发什么函数,往往不会直接写相等,而是两者的差值小于多少时即生效
floata,b;........if(a==b){
//这种写法不建议}........if(abs(a-b)0.){
//一般这么写}
二进制与十/十六进制的转换
刚才那个方法就可以直接算.还有其他算法这里不讲.先让我贴一个表格/p>
BIN
DEC
HEX
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
A
11
B
12
C
13
D
14
E
15
F
BIN是二进制,DEC是十进制,HEX是十六进制.都是英语简写.
十六进制和二进制可以直接换算.方法是每四位二进制看作一个十六进制
比如
6DB9
转换表背过的话读代码快些.因为一般情况下,为了让一行代码看的不至于太长,人们会用十六进制代表二进制.尤其是对于拥有32位cortex-M3内核的STM32F1系列单片机,一次写一个三十二位数属实太冗长.
c/c++中,默认写的数字都是十进制.二进制应该是0b开头,比如0b,而十六进制是0x开头,比如0x3C.
//一般这么写GPIOB-CRL=0x0044;//这么写就不太美观了GPIOB-CRL=0b100;
数学差不多了.开始正文.
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位.比如010就是.当溢出的时候会发生什么呢?
ab;//右移.和上面一样的功能.
ab;//按位求与(AND),比如
//
//------------
//
a
b;//按位取或(OR)
~a;//按位取非(NOT)
!a;//逻辑非(注意和上面的区别)
c+=a;//相当于c=c+a;其他算符同理.
重点看左右移和位操作(与,或,非等).
3.我们配寄存器(register)到底是在配置什么
首先,你的最终目的都是使用单片机的GPIO(generalpininputoutput)读取/输出一个高电平还是低电平.不管是诸如I2C,SPI的通信,还是按键读取,亮灯报警,说到底都是高低电平的控制或探测.
ADC输入的是模拟(Analog)信号,但是会被转化为数字(Digital)信号,一样是高低电平.这里暂且不谈外设,时钟,或者一些功能的使能及配置一样是通过寄存器的.原理相似.毕竟芯片集成电路也是电路,而且是数字电路.暂时不深入.
所以,为了让某一个Pin输出电平,或者使能一个通道,我们可以用0或者1实现.
但是代码终归是代码,不是魔法.为了使需求生效,单片机将每一个需要控制的量,赋予一个地址.在电路层面上实现相关的功能绑定.用户只需通过给这个地址去写一个值,就相当于控制了需要控制的东西.易于计算,我们的32位处理器最大可以寻址4GB的内存空间.
注意:并不是每一个地址都是有真实物理地址对应的.换而言之,一个地址可能指向的是真实的内存,也可能并不是真实存在的内存.不过访问这个地址相当于控制了被控量一样.此时该地址可以看作控制量的一个句柄(handler)."使能"的意思是enable.反义词是"禁用(disable)".计算机相关的词汇总是这么的看不懂字面意思.看英语就明白了.内存是存储数据的地方.任何电子信息数据都可以看做01串.32位机的最小存储单元是DWORD(双字,DoubleWord),包含32比特位.32位机的内存单元可以看作一个个存储32比特位的小仓库.为了找到所需要的数据,需要给这些仓库编号.这个所谓的编号就是地址(Address).存储的值是地址值的变量叫做指针(pointer).比如一个仓库放着一个记录某个货物的多个存放仓库的编号,那么这个内存里的数据就是一个指针.计算机无法分辨哪写是指针,哪些是数据.这需要人去完成.
库函数是寄存器的封装.不管是ST出的HAL硬件抽象层库,还是标准库STL,都是封装而已.本质上是宏定义替换和一些函数.宏定义在编译(