你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

从一无所有学习stm32

[复制链接]
zero99 发布时间:2016-6-6 17:36
     我在想很多学习stm32的,当时为什么学习stm32他也不知道,我们所知道的就是各个论坛讨论stm32的很多,而我们很多人之所以学习stm32是很多的淘宝卖家做了大量的图片文字宣传,于是我们经不住诱惑就买了板子,然后我们就开始了我们的学习之旅。
    在淘宝卖家的眼里有着齐全的入门资料是板子的最大的卖点,于是当我们拿到开发板的时候,我们可以什么都不用做,直接使用已经建立好的工程模板,或者我们想学习下的话就按照他们的教程拷贝几个文件然后添加下,然后我们就以为我们的stm32入门了,心中暗喜stm32不过如此,哈哈!其实这就是曾经的我,但是随着慢慢的学习一方面我们失去了兴趣,感觉这玩意太乏味,另外一方面心中的恐惧随之而来,我们感觉我们永远不能和别人说自己熟悉stm32,因为脱离了网络的资料我们什么都做不了,这是我们最害怕的事情。
    今天我就就像在这里记录下自己一无所有建立stm32工程的过程,是我自己的一个探索过程,同时也是大家互相交流的过程。

5 x7 \: D/ s& t# }
第一步:当然是新建一个工程我把它命名为small(这个随便你,青菜萝卜各有所爱……)

" ?# T7 v+ u; \1 D' W  T 11.jpg
  K6 {! [7 J- B3 E1 A# y. E$ K5 b& f& {2 s1 ^$ w6 |
第二部:就是选择芯片的型号了,这个按照每个人手上的板子的不同就选择不同的芯片型号。

+ u" S/ x/ Q! B0 z" l* T
选择完了之后按确认,然后会出来一个对话框,是问你要不要添加启动文件的,这个简单我们都是白手起家了,别人好不容易送你点东西我们当然照单全收,直接点是啊!哈哈。。。。。
点完是后我们的工程就是差不多建立好了,大家可以看到里面就一个代码文件,是以.s结尾的,是一个启动文件里面的代码全是汇编的,看了有点晕死。。。。,以后再说吧这个。。。。
" r% \7 u3 x6 E
12.jpg ( [9 `! r9 X) i" n! n' v

6 s3 R% `: K  F3 D3 \' ^
要不我们编译了看看结果?

! G$ p1 A0 I! C, N9 P7 u
13.jpg
% T. G1 _8 ~8 a4 @% G2 G/ w
/ L% k+ u2 t' S1 B1 \
+ h! L$ ~9 _* N! p
一串鸟文的错误,看不怎么懂,但是好像是说什么没有main函数。想想也是啊,自己确实没写main函数,要不我们自己建立一个.c文件,然后写一个main函数?说做就做。。。。。
直接点击file下面的新建图标,然后写一个名字保存,注意别忘保存好之后在工程里面“add files to group”,然后我们再自己写一个空的main函数,大家看看我做的对不对?

4 n; t, b! x, ^3 {9 a& U
9999999999999.jpg
) U% T7 Q  V8 e* e: Q# P+ ]
这下可以编译了吧?

, G) U; V8 x, l: H8 d, _7 v; R7 A
15.jpg
0 w. i; T0 k& ?" n+ D& m" K
编译有警告说什么main函数的返回值必须是int。。。。不知道为什么?
百度看了下好像是编译器和c语言标准规范的问题,没办法那就修改下main函数的返回值吧,把前面的main前面的void变成int就好了。
编译通过了。。。
* Y' F* P4 N1 T# h8 o$ V
6 m# d+ _+ W6 U1 m/ z. _5 ?' {( Y4 v
要不我们接下来设置工程属性看看?这个是很多教程中有的,我不想多少,大家自己找找吧。。。。

, h0 @: T9 R: c5 V! i

' W! O( C! D# e6 O( D' ]6 k! Q
16.jpg

1 \* N3 x8 Y0 ^1 r- A

; w) Q% y9 B( c
你看都可以仿真,哈哈那就说明系统运行起来了。。。。但是我们什么都看不到,接下来我们的任务就是想办法点亮一个led灯。
首先我们要控制灯的话就要操作寄存器,还记得我们在51里面要操作P0口吗?是用P0=0x00,这里面简单的说下51里面操作IO口的原理,我们看到这里有一个P0,这个P0是哪里来的?显然不像是int一样是是c语言本身自带的,也不是某一个变量是我们自己定的,其实这个是在reg52.h里面定义的,有图有真相。

+ E- j3 O/ M$ V$ W! d& C
17.jpg 1 O! _0 ?# H9 J3 p* l
3 O! {4 v; |' O1 b) B9 N
可以看到在我们一直使用reg52.h里面他做了这样一件事情,就是把我们的真实物理地址是0x90的这个P1寄存器和P1这个代名词相互联系了,其实P1只是一个代名词,假如我在reg52.h里面修改 把sfr P1 = 0x90;修改为sfr XX = 0x90;这样也是可以用的,只不过下一次你要对P1口进行操作的时候要写XX=0x00;了,所以为了好记我们就把名字取成P1,现在我们知道了51的原理,我们可以依葫芦画瓢来操作stm32的GPIO,先不管我们要操作什么寄存器,我们要接解决的第一个问题怎么使用c语言操作单片机中知道绝对物理地址的寄存器,比如我们通过查资料RM0008的179页知道了关于IO操作的一个寄存器GPIOA_CRL的绝对地址是0x40010800+0x00(其中0x40010800是起始地址,0x00是偏移地址),接下来我们怎么操作他呢?用 sfr?好像sfr是51汇编特有的指令,在arm里面没有。。。这时候我们是否想到c语言的一个和地址紧密相关的内容----指针,我们在上课的时候知道指针的本质就是地址,这样我们是否可以通过它来把实际的物理地址和c语言变量建立关系呢?首先我们要把绝地地址变为指针变量 ,肯定是强制类型转换了(int*(0x40010800+0x00))通过这一步我们已经有一个int类型的指针,这个指针指向的地址就是我们GPIOA_CRL的绝对地址(0x40010800+0x00),有了指针之后我们要取变量,那么很简单只要一个简单的*取变量运算符就可以了(int*(0x40010800+0x00)),这下变量有了那么我们是不是需要给变量取一个名字啊?
那就这样#define GPIOA_CRL *((int*)(0x40010800+0x00)) 好了完成了我们终于可以再c语言的环境中操作我们的寄存器了,接下来我们只要给GPIOA_CRL 这个变量赋值就是我们在给GPIOA_CRL 这个寄存器赋值,先休息下再说。。。。。
    这篇文章其实我是一边写代码做测试一边写的,就在上面我想要控制IO的时候花了好长的时间,就是因为自己的不仔细吧!犯了几个小错误。先看我的最终代码。

% a/ V8 k8 w: {+ ~# g
18.jpg

$ M9 L* r2 K4 l
代码非常的简单,但是为了这几行代码花了我好久的时间,我已经在代码后面写了注释了,具体的怎么来的让我婉婉道来,首先是上面那几行的define的机构我在上面已经介绍过了,具体的地址是怎么知道的?下面我来简单的说下,在stm32的
RM0008 6 z2 d+ M; k  ?: H) d9 e
Reference manual
5 K& }* l3 U: k' q  {# X* }STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx 6 o! m+ }* Q4 d3 k9 {, ?
and STM32F107xx advanced ARM-based 32-bit MCUs
这个开发文档中的44页内存映射表,如下图

/ l: q# P* j% m' j' f* r! m& \' C
19.jpg

# y+ x+ P  h* V/ j% q( H
我截图的只是上半部分,还有下半部分,在下半部分我们可以看到

$ B  a1 \: P: a4 v, U' ~ 20.jpg ) L3 H0 r7 J) l/ ]

) c+ R0 ^+ E8 [* d; U3 F0 K/ ?3 n
从这里可以看到和GPIOA相关的寄存器的起始地址是0x40010400所以代码中的一开始的基址是这个,然后我们需要知道不同的寄存器的偏移地址,这个是在后面的跳转的链接可以看到的,跳转到179页,偏移地址的表格
这样的话上面的几个define的计算也就水落石出了。
        这里简单的说下自己当时犯的一个错误。我一开始写的#define GPIOA_CRL *((int*)0x40010800+0x00) 是这样的,调试后死活不是我想要的工作效果,后来花了好大的力气才想起来原来是宏定义的括号的问题,大家仔细比对下我在代码中的写的和我上面的写法,发现什么端倪没?#define GPIOA_CRL *((int*)(0x40010800+0x00)) 看到红色的括号没?就是因为一个小小的括号耽误了很长时间。
    花了这么长的时间我们终于可以操作IO口了,那怎么操作呢?在学过51的人的眼里那是很简单的,就是直接把我们要IO口输出的值送到数据寄存器中不就好了,但是毕竟他是高级的stm32,所以要复杂点,首先我们要开启GPIOA的端口时钟,这也许大家会疑惑了,怎么还和时钟有关系了。。。。还是看资料吧!
4 O1 @0 I; J- l$ W4 p
21.jpg

3 W8 P9 h6 u& Q
可以看到GPIOA是挂载在AHB2总线上面的,他们都有自己的时钟信号的控制端,这是由stm32的机构决定的,我想之所以这样做,一方面可以降低系统功耗,让工作的模块的时钟使能,不工作的就不使能,其实这里的时钟信号就好比是模块的心脏一样,只有先让他工作了我们才可以去对他进行操作,这就是代码上     RCC_APB2ENR=0x00000004;//开启GPIOA的端口时钟这一句的作用,我尝试过,假如去掉这一句话的话,即使我后面对寄存器赋值了,也是没有作用的。所以这一句很重要,而且与下面的顺序是不好交换的。接下来就是设置GPIO的工作方式什么输入输出 模式之类的,对于只学过51的人来说有点新鲜感觉,如果学过其他高级点单片机的 估计已经习以为常了,就那么回事,具体的寄存器的每一位我就多说了,直接看stm32f10XXXX参考手册的113页上满写的很清楚了。
这里再顺便解释下下我看到的一个现象,先看文档
, O5 u6 [7 y; x8 s
22.jpg

1 l% v. ?, D; i/ S  v/ `  x# {
从这里面我们知道任何IO在复位之后都处于浮空输入状态,这是PDF上说的通过我的代码大家看到我只是改变GPIOA0这位的状态所以其他IO口应该还是浮空输入的模式,但是实际上是的吗?
看仿真截图:
          大家可以看到其他几个口还是听话的就是PA12 PA13 PA14 PA15不怎么对劲,怎么回事呢?一开始我也疑惑,后来突然想起来了,这不是我们仿真用的jtag口吗?这样的话就对了,于是为了验证我的想法,我查看jtag用的其他IO口的情况,都不是默认的浮空输入模式,这样的话就应该是这个原因了。(提醒大家一下以后设计硬件的时候尽量避免使用jtag口,如果实在避免不了的话在设计程序的时候就要注意关闭jtag模式释放那几个IO口,在此做一个友情提醒因为被坑过几次。。。)
      代码写完了,然后编译下载,不出意外的话就可以看到PA0上面接的led灯亮了,这是必然的结果,这一次入门教程也就差不多了!(来自散吧散吧的分享)
8 t5 t; t) M3 N, e- y
收藏 3 评论13 发布时间:2016-6-6 17:36

举报

13个回答
lzts 回答时间:2016-6-7 08:21:55
顶一下了. 我都是用MDK配合工程模板来开发的
Paderboy 回答时间:2016-6-7 08:49:33
哈哈,小久小号不错。。。。
yfy123 回答时间:2016-6-7 12:44:02
厉害厉害!
mmonp 回答时间:2016-6-7 13:23:48
不错 嘿嘿
any012 回答时间:2016-6-7 13:59:10
一无所有...
风丛林 回答时间:2016-6-7 15:06:34
佩服,抽丝剥茧地学习,深刻
zero99 回答时间:2016-6-7 15:07:11
Paderboy 发表于 2016-6-7 08:49
  t2 }' C1 `0 l& M$ W2 G) J哈哈,小久小号不错。。。。

: ~* q9 U6 w( V% }
西街耳呦 回答时间:2016-6-14 13:30:04
刚刚学习,还没入门
又是一年春来到 回答时间:2016-6-15 15:01:34
支持支持
S相遇的邂逅S 回答时间:2016-6-15 20:52:25
感觉这种方法好溜
xosliu 回答时间:2016-6-18 09:20:28
回归真学习,不为老板学。
kaierwen 回答时间:2016-6-20 14:47:14
accelerating 回答时间:2016-6-20 15:59:23
谢谢楼主

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版