怎么看待今年出现了很多非专业但是需要干嵌入式或单片机工程的人
///插播一条:我自己在今年年初录制了一套还比较系统的入门单片机教程,想要的同学找我拿就行了免費的,私信我就可以哦~点我头像黑色字体加我地球呺也能领取哦。最近比较闲,带做毕设,带学生参加省级或以上比赛///
0.绪论
对于经过系统培训的开发者.单片机或SoC的驱动开发,不管是使用各种库还是直接上寄存器,都不成问题.
可很多非专业但是需要干嵌入式或单片机工程的人,比如机械,车辆工程,物理,或是其他的一些专业.有时候这些学生需要搞比赛,做项目,不可避免的要用到一些单片机(由于种种原因,现在几乎都会首选STM32).但是缺乏系统训练的学生往往无法看懂寄存器版本的例程,或是别人的开源项目.自己写的话更是不知道如何开始.或者编译完成后总是出现莫名的问题.这给大家带来了极大的困难.
同时互联网上大部分教程都是转载来转载去,各种差异和版本都有.很多人即使看过了也是一头雾水.所以应一个同学要求,大概写一下寄存器的一些操作.
本教程将会使用"学生用户体量庞大"的STM32F1xx系列的单片机作为例子.但是不必担心.所有的寄存器操作都是共通的.并不会有本质的差异.希望你能快速阅读完并且理解完,然后查阅自己需要的资料.不管是什么东西,这种方式都是通用的.
嵌入式软件开发具有一个比较庞大的知识体系.限于作者的时间和水平,本文不可能讲太多东西.但是如果你只是因为不会寄存器开发而陷入瓶颈,那么本文将会有较大帮助(毕竟这个是写文章的目的).
理解本文需要一些基础,大概包括: c/c ,一些数学,计算机基础知识.大多数理工类专业都会有相关的课程的.如果确实存疑可以即时搜索.
让我们先记住开发的正确方式: 摘抄借鉴,修改糅合,以实现功能为目的,以别人的代码为基础.
只要不涉及什么知识产权的话,这样做是完全没有问题的.毕竟不需要把很多基础性的东西写来写去.所以请大胆的找开源项目,尽可能在基础上改,而不是从零开发.不废话了,开始正事.
1.数学:数制,进制换算
数制也称为“计数制”,是用一组固定的符号和统一的规则来表示数值的方法。任何一个数制都包含两个基本要素:基数和位权. [1]任何一个数字都能这样表示
计算机常用2进制.就是只有1和0的数字.其中1一般认为是高电平表示true,逻辑真值, 0一般是低电平, false,逻辑假值.
定理:在任意进制下,不一定所有的有理数都可以使用有限位数的小数形式表示.
比如十进制下的0.1就是二进制的无限不循环小数.上面那个例子也是.
著名的 IEEE754 float浮点数标准导致的bug:在很多语言中, 0.1 0.2≠0.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
转换表背过的话读代码快些.因为一般情况下,为了让一行代码看的不至于太长,人们会用十六进制代表二进制.尤其是对于拥有32位cortex-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输出电平,或者使能一个通道,我们可以用0或者1实现.
但是代码终归是代码,不是魔法.为了使需求生效,单片机将每一个需要控制的量,赋予一个地址.在电路层面上实现相关的功能绑定.用户只需通过给这个地址去写一个值,就相当于控制了需要控制的东西.易于计算,我们的32位处理器最大可以寻址4GB的内存空间.
库函数和寄存器可以混着用吗?
当然可以.本身使用库函数的项目里可以直接引入寄存器的代码.因为库函数本身也用到了寄存器.但是寄存器要使用库函数,需要先将库函数导入到工程才行(编译链的makefile文件里).当然都可以混着用.
7.这些没问题了,但是那些OD开漏输出,推挽输出,浮空输入,上拉输入,都是什么意思......
,