找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1803|回复: 0
收起左侧

ARM嵌入式学习之-----潜龙勿用

[复制链接]
ID:105323 发表于 2016-2-24 18:00 | 显示全部楼层 |阅读模式
天下难事必作于易,天下大事必作于细----------有感于网络连载学习教程《嵌入式Linux学习手册》开篇之语,故也将之作为步入嵌入式学习的开篇词以自勉-  
我29了,当立而未立,现在开始确实太晚了,可我别无选择,性格决定命运,命运将我推到了这个时代的帝都,我别无选择,还好这是我的兴趣所在,so...
​#include“hope.h”   
​​#include“ persistence.h”
#include“ wisdom.h”

initialise......
START UP
main( )   
-------------------------------------------------------------------//2015/5/20晚追加  
1.ARM 系列CPU与对应嵌入式操作系统的关系

要准备涉足基于ARM的嵌入式操作系统了,面对各系列内核的ARM处理器与嵌入式操作系统,一直搞不清为什么嵌入式linux无法在诸如51和ARMcortex M4系列CPU上运行,经过一夜的查询终于搞清楚了,关键在于CPU是否具有MMU,ARM cortexM4是一个主打控制功能兼具一定DSP功能的16/32位MCU,不具备MMU,所以只能运行如uc/osII这样的操作系统,而ARMcortex A系列CPU则具备MMU功能,也同时具备更快的运行速度,所以可以运行完备的嵌入式linux操作系统。​
夜深了,明早继续消化,编辑........​
2015/5/20----------------------------------------------------------------------------------
2.MCU --​STM32F407具备OTP(OneTime Programable)存储器
MCU按其存储器类型可分为MASK(掩模)ROM、OTP(一次性可编程)ROM、FLASH ROM等类型。MASKROM的MCU价格便宜,但程序在出厂时已经固化,适合程序固定不变的应用场合;FLASHROM的MCU程序可以反复擦写,灵活性很强,但价格较高,适合对价格不敏感的应用场合或做开发用途;OTPROM的MCU价格介于前两者之间,同时又拥有一次性可编程能力,适合既要求一定灵活性,又要求低成本的应用场合,尤其是功能不断翻新、需要迅速量产的电子产品。​
2015/5/24
3.C语言#ifdef的用法
​C语言知识不精啊 刚开始代码分析就遇到坎坎了,一点点回忆,一点点积累吧~~
​#ifdef是C语言中预处理功能(宏定义,文件包含,条件编译)中的条件编译功能,具体格式如下:
#ifdef   x             #ifdef   x     
................               #definex
.................​               ................
#endif​  x            #endif   x
作用是先测试X是否被宏定义过,如果没有的话则条件指示值为真,此时执行​#ifdef  x与#endif之间的语句,因为没有被定义过,所以中间的执行语句一般加入#define x进行宏定义;反之则跳过,执行#endif x后的语句。
条件指示符最重要的作用在于防止头文件的重复包含和编译​​
​千万不要忽略了头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。   还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:   #ifndef<标识>   #define <标识>   ......   #endif    <标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:
stdio.h    #ifndef_STDIO_H_
......  
#endif
#ifndef xxx//如果没有定义xxx
#define xxx//定义xxx
#endif //结束如果  
这个用法主要是在头文件中,主要是为了防止类重复的include,所以在类的头文件之前加上前面两个,用类名替代xxx,在最后加上最后一句。

更全的解释在百度文库中《C语言之详解#ifdef等宏及妙用》放在kindle零碎时间看​
2015/5/25----------------------------------------------------------------------------------​
今天晚上没有加班,把delay.c中的各种延时函数给看完了,同时也对UCOS有了一定理解,让我惭愧的是连C语言中的!,~意思都记不大清楚了模模糊糊能猜对意思,就是不大确定,这记性。。。
2015/5/28----------------------------------------------------------------------------------​
在CM3/CM4中,SRAM最低1M地址范围与片内外设的最低1M地址范围具备位带(bitband)功能,也就是说最低1M地址范围的每个bit位地址都可以用一个32位字的别名来表示,引入位带操作,其中一个好处就是可以对单个IO口进行操作,最简单就是通过将0或1赋值给单个IO口,控制其输出状态,具体推导公式见手册;
汇编编程:在ARM的RISC指令集中,数据从内存与寄存器中移动,只能通过LDR/STR​进行   不能用MOV指令,MOV指令只能用于寄存器之间数据的转移,或者立即数与寄存器之间的转移;
在X86的CISC指令集中,是可以用MOV指令在内存与寄存器之间进行数据转移操作的。
这是RISC与CISC之间比较大的不同之处。​
ARM汇编指令:ORR.W 是对32位数进行或操作;不加是对16位数进行或操作。​
2015/5/31----------------------------------------------------------------------------------
不求甚解是我最大的毛病,今天看程序,有一个地方不明白,本想放过去以后再说,但好奇心还是促使我查了​一下,这一查才知道,这个点竟是如此重要,用某博客释疑文章来说“我认为这是区分C程序员和嵌入式系统程序员的最基本的问。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量.......”看完整篇文章后确实受益匪浅,也暗自庆幸自己没有草草放过。故下面将原文转发:
__I、 __O、__IO是什么意思? 这是ST库里面的宏定义,定义如下:
​#define__I volatile const  
​#define__O volatile  
​#define__IO volatile
显然,这三个宏定义都是用来替换成volatile 和 const 的,所以我们先要了解这两个关键字的作用: volatile 简单的说,就是不让编译器进行优化,即每次读取或者修改值的时候,都必须重新从内存或者寄存器中读取或者修改。 一般说来,volatile用在如下的几个地方: 1、中断服务程序中修改的供其它程序检测的变量需要加volatile; 2、多任务环境下各任务间共享的标志应该加volatile; 3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义; 我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
1)一个参数既可以是const还可以是volatile吗?解释为什么。
2);一个指针可以是volatile 吗?解释为什么。
​3);下面的函数有什么错误: intsquare(volatile int*ptr) { return*ptr **ptr; }
1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
​2);是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3)这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
intsquare(volatile int *ptr)
{ inta,b;
​a= *ptr;
b= *ptr;
returna *b; }
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!
正确的代码如下:
longsquare(volatile int *ptr)
{ inta;
a= *ptr;
​returna * a; }
const
​只读变量,即变量保存在只读静态存储区。编译时,如何尝试修改只读变量,则编译器提示出错,就能防止误修改。
const与define 两者都可以用来定义常量,但是const定义时,定义了常量的类型,所以更精确一些(其实const定义的是只读变量,而不是常量)。#define只是简单的文本替换,除了可以定义常量外,还可以用来定义一些简单的函数,有点类似内置函数。const和define定义的常量可以放在头文件里面。(小注:可以多次声明,但只能定义一次) const与指针
intme;
constint * p1=&me; //p1可变,*p1不可变 const 修饰的是*p1,即*p1不可变
int* const p2=&me; //p2不可变,*p2可变 const 修饰的是p2,即p2不可变
constint *const p3=&me; //p3不可变,*p3也不可变 前一个const 修饰的是*p3,后一个const 修饰的是p3,两者都不可变
前面介绍了volatile 和 const的用法,不知道大家了解了没?了解了后,下面的讲解就更加容易了:
__I:输入口。既然是输入,那么寄存器的值就随时会外部修改,那就不能进行优化,每次都要重新从寄存器中读取。也不能写,即只读,不然就不是输入而是输出了。
__O:输出口,也不能进行优化,不然你连续两次输出相同值,编译器认为没改变,就忽略了后面那一次输出,假如外部在两次输出中间修改了值,那就影响输出
__IO:输入输出口,同上
为什么加下划线? 原因是:避免命名冲突 一般宏定义都是大写,但因为这里的字母比较少,所以再添加下划线来区分。这样一般都可以避免命名冲突问题,因为很少人这样命名,这样命名的人肯定知道这些是有什么用的。 经常写大工程时,都会发现老是命名冲突,要不是全局变量冲突,要不就是宏定义冲突,所以我们要尽量避免这些问题,不然出问题了都不知道问题在哪里。​
2015/6/1----------------------------------------------------------------------------------
​遇到很多IO口设置程序都有GPIO_TypeDef*GPIOx  就如同下面这句一样
​voidGPIO_AF_Set(GPIO_TypeDef* GPIOx,u8 BITx,u8 AFx)费了好一番脑筋才看明白
其实意思是定义了一个结构体指针类型的变量GPIOx,类比于后面的​u8BITx,u8 AFx
请看下面一段程序
  __IO uint32_t OTYPER;
  __IO uint32_t OSPEEDR;
  __IO uint32_t PUPDR;   
  __IO uint32_t IDR;     
  __IO uint32_t ODR;     
  __IO uint16_t BSRRL;   
  __IO uint16_t BSRRH;   
  __IO uint32_t LCKR;   
  __IO uint32_t AFR[2];
} GPIO_TypeDef;
#define GPIOA             ((GPIO_TypeDef *) GPIOA_BASE)   //
void GPIO_AF_Set(GPIO_TypeDef* GPIOx,u8 BITx,u8AFx)
{  
GPIOx->AFR[BITx>>3]&=~(0X0F<<((BITx&0X07)*4));
GPIOx->AFR[BITx>>3]|=(u32)AFx<<((BITx&0X07)*4);
}   
​该段程序的作用是对GPIOA的寄存器进行设置,
首先通过 ((GPIO_TypeDef*)GPIOA_BASE)对GPIOA_BASE(A系IO口的基址)指向结构体GPIO_TypeDef的起始地址,并且用宏替换成GPIOA
  #defineGPIOA             ((GPIO_TypeDef *) GPIOA_BASE)
GPIOA是一个GPIO_TypeDef型的 结构体指针变量具备GPIO_TypeDef所有的成员变量类型这样通过GPIOA基址加上不同成员的地址偏移量访问不同的寄存器,对GPIOA的各个IO口进行设置,如果不#defineGPIOA             ((GPIO_TypeDef *) GPIOA_BASE)
那么GPIOx->AFR无法采用此方式访问成员变量,所以关键在于强制转换成结构体指针变量。
-----------------------
​今晚照着葫芦画瓢把LED等点亮了,关键在于又解决了一系列的小问题主要列举下面两个:头文件包含#include一定别忘了了把包含文件的路径给加进去  否则会提示找不到文件无法打开无该文件路径等等;每个程序文件的末尾加一个空行,作为编译器识别文件结束的标志虽然只作为警告,但为追求完美,作为一个编程习惯还是回车一下,加上一空行为宜。
夜深了,又该睡了,下次安排在晚上10点半结束学习,留半小时总结一天之得失​
2015/6/2--------------------------------------------------------------------------------------
​今晚蜂鸣器实验:第一,我的较真精神应该表扬,不单单满足于完成蜂鸣实验,对于输出是上拉还是下拉也进行辨析,结果不论是上拉还是下拉,蜂鸣器都响,其实由原理图得知,蜂鸣器响主要与拉电流有关,IO口向外送电流催动三极管导通,蜂鸣器鸣响。
那么再巩固一下拉电流跟灌电流的概念​​,为加深理解,给出其相对应的英文翻译,就一目了然了
拉电流(sourcingcurrent),对一个端口而言,如果电流方向是向其外部流动的则是“拉电流”​
灌电流(sink current),对一个端口而言,如果电流方向是向其内部流动的则是“灌电流”​
22:30  结束反思总结完睡觉。
记得每个周末都重新回顾一遍。​
2015/6/3------------------------------------------------------------------------------​
按键输入实验,没看明白,​​不过关于IO口上拉下拉的设置有一些形象的认识,此实验,IO口作为输入,按键按下,IO口接地,IO口向地输出电流,则IO设置为输入上拉(sourcingcurrent)模式;反之按键接3.3V电平,按下按键后,IO口接入3.3V,这样从3.3V向IO口灌入电流,这样则IO设置为入下拉;如果设置IO口输出模式,上下拉取决于是拉电流还是灌电流。
​2015/6/7--------------------------------------------------------------------------------
串口通信实验中,在输入代码的时候,由于括号用了汉字状态下的括号,编译器提示出错:expected a “)”或者source file is not valid UTF-8   经过一段段的程序粘贴差错定位到出错语句的 ;
一溜烟儿,竟然连续三天没有研究了,真可怕​,时间快的真可怕
2015/6/8--------------------------------------------------------------------------------​
​今天要转变学习方式了,发现回顾前面看的章节时候,依然没什么深刻的印象,可能虽说是按一个个小工程来进行练习的,也跟看书一样,稍微零碎一点,依然不系统,松散,故先粗看一遍,然后直接上马我的工程,由顶至下的设计,各个模块击破,最后再进行连接,这样效率更高,针对性更强,印象更深刻。
就这样,摸着石头过河吧
时间真的有些捉襟见肘,必须不断想办法去提高效率。​​
2015/6/10​-----------------------------------------------------------------------------------
最难啃的一块骨头,TFTLCD液晶显示,我应该抽出一段大的连续时间块,把它啃下来,有史以来比较复杂的设备驱动程序​
2015/6/25
好吧,一眨眼已经15天过去了,一对比才发现时间是如此经不起蹉跎,真的过得太快了,期间有几天看了,但没勤于记录,今日想起,已经两个周过去了~~~真是 快得令人唏嘘啊
还是液晶屏的驱动问题,在线调试无法持续运行,总是陷在一个延时程序中出不来,我也试图查找其原因,未果,于是在正点原子论坛上发了我第一个处女帖,希望早日获得解答,以解惑,以及解决问题的方法
2015/07/01
程序中的extern关键字
extern置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量或函数时,在其它模块中寻找其定义。另外,extern也可用来进行链接指定。
​同一个变量或函数在程序中只能定义一次,如果程序文件A跟B都定义了该变量​或函数,那么在编译的时候是没问题的,因为编译时是按文件编译的,各个文件中定义的全局变量是互不透明的;当进行到链接阶段时,要把所有文件融合到一起,就会出现定义两次的情况,就会报错;
​extern的原理很简单,就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,你要放行!”
所以extern是一个该变量或函数在别处定义的一个声明,可以多次出现;而定义却只能出现一次​因此在某个文件中引用一个全局变量的时候,需要用extern声明一下

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表