第一篇:裸机开发学习心得
裸机开发学习心得
首先说明,我没啥经验。只是说一下我的体会。也没有朝谁开火的意思。如果无意伤了谁,还望海涵;如果所言有误,还请高手多多指教。好了,开始忽悠~~~~~~~~~~~~~~~~ 在论坛里见到好多朋友学习裸机的时候都有一个习惯——做裸机开发时,过度依赖天嵌的裸机测试程序。怎么说呢,我觉得这样不是很好。尤其是对于一个新手。
也许是学习单片机出身的原因,我有一个毛病,就是对别人的代码不放心、凡事非得扣到底层。
一开始我看天嵌的裸奔三部曲,有一个体会——越看越迷茫。尤其是简单地修改了天嵌的程序,实现了显示几个自定义图片的时候。这里也算是给天嵌提一个建议吧。一般来讲,新手看到这里的感觉就是特迷茫:难道这就是学裸机程序吗?修改几行C代码?天嵌手册里说这样会迅速建立成就感,可是我丝毫没有。反而感到的是强烈的不安。毕竟天嵌的裸机程序对于一个新手来说太庞大,甚至会搞不清文件之间的关系。我们现在做的就是简单地修改几行现成的代码,然后看看效果。我就在想,此时我只会修改几行代码。底层硬件、ARM是如何启动的、启动代码2440init.s是如何支持ARM启动并跳转到C函数里的?这些东西都被天嵌的裸奔三部曲教程透明掉了,这些我都不会,我们只是做了做应用层的修改。而那些被透明掉的知识,现在完全都是依赖于天嵌的测试程序的。试想,一旦一天老板让我做一个新的东西,新的硬件配置,没有了天嵌裸机程序里的底层知识,我们将如之奈何?基本就废了吧。
也许是由于原先是搞单片机的缘故,所以总喜欢扣底层,做Linux或者WinCE上层应用的朋友也许会觉得我在这里瞎折腾,做无用功。但是,对于从单片机转过来的人来说,我说的这些是最重要的——他们要搞清楚ARM是如何工作的,如何用程序去控制硬件,如何靠自己的力气搭建出来一个裸机程序。和做上层的人不一样,做底层的人就喜欢扣这些东西,因为这些东西如果搞不懂,那搞其上层来就会觉得发飘,或者说总是不知道地下发生了什么导致心里没底。最要命的是以后做Linux设备驱动的时候。由于对底层的认识不够,必将导致非常吃力!
这些天狂啃了一顿启动代码2440init.s。忽然意识到,裸机应该换一个方法学。
第一步、狂啃启动代码。
啃过之后你就会发现,原来大家在三星原版基础上改的,然后互相抄,有的甚至都抄错了。这可能是个痛苦的过程,你要找各种版本的2440init.s,但却是必经的。一旦走过,你会发现你对ARM底层就有了和以前不是一个深度的理解。以后做起开发来底气也就足了。这个过程里,你会遇到很多汇编
的基础,杜春雷的那本《ARM体系结构编程》就是本很不错的材料。但是千万记住,你拿它当字典用就行,千万别运气下决心要从头到尾看完。不然你真不知道你是怎么死的。以前还有人说一定要通读三星手册,我觉得这跟杀人没什么两样。学习一定要有目的性。当学习启动代码遇到问题时,再去查汇编语法和三星手册时,学习时最快的,记得最牢的。如果漫无目的地通读。。就算最后你活下来了,你也不知道之前你都看了些什么。。
第二步、跟着启动代码跳转到C程序来。
这个过程就像搞单片机一样了。自己写自己的第一个跑马灯程序、蜂鸣器程序、按键中断程序、定时器、LCD etc etc etc etc etc。最后你再把自己写的所有的东西组合起来,构建成一个属于自己的裸机测试程序。经历过这个过程,你就会发现,原来ARM不是不可战胜的。原来,你已经一步一步踏实的走过来了。这个时候,你回头再看一眼天嵌的逻辑测试程序,你就会淡然一笑,哦,这个啊,我自己已经写出来了。
至此,所谓“成就感”才建立起来。至此,你才可以丝毫不依赖别人地做出自己的东西。至此,你才学到了真正的东西。
因此,在这里我诚恳地向天嵌提出建议。如果写裸奔教程,请从最底层写起。贵公司对启动代码的介绍少之又少。而完全是在对上层做文章。我想,对于一个ARM新手,不管他之前学没学过单片机,你让他过度依赖测试程序的框架做开发,他真的会因为底层基础不好而发飘,导致以后的全部开发都过度依赖现有的测试程序。从本质上讲,他还是没有学懂裸机。在没搞懂启动过程的前提下,就去学习裸机电子钟之类的东西,结果只有一个字——飘。
我知道天嵌写教程也要考虑篇幅,也不能面面俱到。面面俱到那真的是强人所难。但是不知道贵公司是否可以考虑换一个思路,换一种引导的方式,哪怕只是写一个启动代码的详细注释,已经如何建立一个启动代码+LED跑马灯程序,都足以给用户一个清晰的方向。以后的教程甚至不写,用户也会沿着思路,学会自己搭建起属于自己的东西,而不是搭建起建立再现有代码基础之上的东西。这样用户学到的东西才真正会变成自己的。最后,想说选本好的教材,这很重要。不好的教材只能让你多走弯路。我觉得阿南的那本《ARM Linux入门与实践》就很不错,我不是在做广告,真的不错。那里多一句废话不写,净捞干的。比如汇编部分,他只介绍了学习启动代码所必须掌握的一些指令和伪指令。其他没用的一概不写,不像有些书,就会抄DataSheet。
韦东山的《嵌入式Linux应用开发完全手册》对于裸机部分
写得其实不是很好,他总喜欢在Linux下搞。而且中断那里写得非常不好。我们大多数人还是在ADS下搞的,于是我们一般还都是用启动代码来启动开发板的(搞裸机也不能太裸不是,启动代码都自己重新写就基本可以直接累死了),启动代码里的非常漂亮的两级向量表中断处理机制韦东山完全没用到。而且那本书每一章里知识高度浓缩,新手看了容易上头。而且,凡是号称什么什么全书的,往往不可能写得很全。嵌入式技术是一个庞大的体系,怎么可能一本书写全呢?如果号称全,则必然不精。
还有,有谁还看中嵌的视频的就别看了。首先我说,他们已经倒闭了,因为骗钱倒闭的。他们的质量很差,视频质量也很差。老师就是在念程序或者念DataSheet。讲启动代码的时候老师屁都不会,之前他们讲过汇编语法,讲到bootloader的时候,bootloader再干什么一点儿没讲,反而又领着学生复习汇编基本语法,这都忍了,可老师硬说bic指令是跳转指令。还有,体系乱套。先讲Linux,然后将裸机。我都不知道他是不是先拉屎后脱裤子。这种东西最好别看。看完他的视频再看什么教程都没法学了。这个和吃完大便之后就再没食欲吃烤鸭是一个道理。
帖子有的地方有点儿攻击性,有点儿偏激。再次往误伤的同胞海涵。如果您觉得我扯了半天都是废话,您就当这是一阵风飘过,或者这是我半夜的梦话。。
OK,今天就说到这儿吧。困死了,睡了。
第二篇:S3C2440裸机学习心得(完整版)
有什么问题欢迎一起交流。以下是部分心得:
LED
GPXCON中每两位控制一个引脚:00输入,01输出,10特殊功能
初始化 rGPBCON , rGPBUP , rGPBDAT 三个寄存器 关于ADS配置的解释:
在Target Settings中的Post-linker中选择ARM fromELF,因为我们需要把ELF文件格式转化为下载到flash中所必须的二进制文件。
在Language Settings中的所有语言的Target下的Architecture or Processor,选择ARM920T,因为s3c2440是ARM920T内核。
在ARM Linker中的Output中的RO Base填写0x30000000,因为在开发板上SDRAM是从0x30000000地址开始。在Layout中的Object/Symbol中填写2440init.o,它是启动文件的目标文件,在Section中填写Init,它是在启动文件中我们所定义的入口代码段的名称。
外部中断
我们要打开某一中断的屏蔽,这样才能响应该中断,相对应的寄存器为INTMSK; 还要设置外部中断的触发方式,如低电平、高电平、上升沿、下降沿等,相对应的寄存器为EXTINTn。
另外由于EINT4到EINT7共用一个中断向量,EINT8到EINT23也共用一个中断向量,而INTMSK只负责总的中断向量的屏蔽,要具体打开某一具体的中断屏蔽,还需要设置EINTMASK。
有一些中断是共用一个中断向量的,而一个中断向量只能有一个中断执行函数,因此具体是哪个外部中断,还需要EINTPEND或rINTOFFSET来判断
使用__irq这个关键词来定义中断处理函数,这样系统会为我们自动保存一些必要的变量,并能够在中断处理函数执行完后正确地返回 这种形式:void __irq key();pISR_EINT0=(int)key;
定时器使用
主要配置 :外部时钟源→通过寄存器MPLLCON得到FCLK→再通过寄存器CLKDIVN得到HCLK和PCLK->再得到定时器的工作频率
PWM定时器使用:
蜂鸣器=GPB0=TOUT0=定时器0 蜂鸣器 高电平响,低电平不响
关键是设置寄存器TCNTBn和TCMPBn,前者可以确定一个计数周期的时间长度,而后者可以确定方波的占空比
定时器中断使用:
不需要配置TCMPBn,只需TCNTBn,TCNTBn*1/定时器的工作频率=定时的时间 但要记住中断要:
void __irq timer0_sever();
pISR_TIMER0=(int)timer0_sever;rINTMSK =~BIT_TIMER0;
Uart
主要配置以下寄存器: UBRDIVn,UCONn,UTRSTATn 收发寄存器: UTXHn,URXHn 注意几点:
1.对于s3c2440来说,接收数据是被动的,发送数据是主动的,因此一般来说,接收数据用中断方式,发送数据用查询方式较好;
2.在中断方式下,当接收到数据时,尽管可能该数据无用,但也一定要读取它,否则下次再接收数据时,不会再引起中断,因为接收数据缓存器被上次接收到的数据所霸占,只要没有读取它,它就永远在那里;
3.由于UART中断涉及到SUBSRCPND寄存器,因此在中断处理程序中不仅要清SRCPND寄存器,还要清SUBSRCPND寄存器,它们的顺序一定是先清SUBSRCPND寄存器,再清SRCPND寄存器,否则就会引起一个中断两次响应的问题。因为是否中断由SRCPND寄存器决定,而SRCPND寄存器的相关状态位由SUBSRCPND寄存器决定
简单的串口控制台:1.键盘输入数据后在控制台上进行回显
recv_data = uart_getc();
return recv_data;
2.让接收的字符组成命令 data_buf[i] = uart0_scanf();if(data_buf[i] == ' ')3回车——命令判断、退格——删除字符、判断命令后进入相应分支进行处理
else if(data_buf[i] == 'b')
if(!(no_system_strcmp(“read iic”,cmd_buf)))
LCD(4.3寸,480*272)
在2440中时钟设置是 FCLK:HCLK:PCLK=400:100:50 注意两点:
1时序,参数的设置
VCLK是像素时钟信号。VCLK=HCLK÷[(CLKVAL+1)×2]
例如,HCLK的频率为100MHz,要想驱动像素时钟信号为6.4MHz的LCD屏,则通过上式计算CLKVAL值
在s3c2440中,所有的这些信号(VSPW、VFPD、VBPD、LINEVAL、HBPD、HFPD、HSPW和HOZVAL)都是实际值减1的结果。这些值是通过寄存器LCDCON2、LCDCON3和LCDCON4来配置
行频(HSF)和场频(VSF):
HSF=VCLK÷[(HSPW+1)+(HSPD+1)+(HFPD+1)+(HOZVAL+1)]
=7.1÷408=17.5kHz
VSF=HSF÷[(VSPW+1)+(VBPD+1)+(VFPD+1)+(LINEVAL+1)]
=17.5÷270=64.8Hz2、显示缓存区
一般我们是通过定义一个与屏幕尺寸大小相同的二维数组来开辟该空间的,这样控制屏幕内容会方便一些,如当屏幕的尺寸为480×272时,可以定义该缓存区为LCD_BUFFER[272][480]。
在s3c2440中,寄存器LCDSADDR1和LCDSADDR2用于设置显示缓存区,即把我们定义的那个二维数组告诉s3c2440。其中LCDBANK的9位数据指定LCD的BANK,即显示缓存区的第30位到第22位地址;LCDBASEU的21位数据指定了LCD的基址,即显示缓存区开始地址的第21位到第1位;LCDBASEL的21位数据指定了LCD的尾址,即显示缓存区结束地址的第21位到第1位。拓展:LCD的字符显示 汉字库 中文字符和ASCII码混合在一样,如何区分它们呢?其实也很简单,ASCII码的最高位是0,而中文的最高位是1,因此当读取到的一个字节的最高位是0,则该字节为ASCII码,它的下一个字节与这个字节无关;当取得到的字节的最高位是1,则表示的是中文字符,并且该字节与它的下一个字节组合在一起表示完整的一个汉字。
把字库变换成一个超大的数组,那么我们就可以像操作数组一样读取字库了 #include “font_libs.h” //内有两个数组__HZK[ ]和__ASCII[ ]
if(String[k]&0x80)//中文字符 qh=String[k]-0xa0;//区号 wh=String[k+1]-0xa0;//位号
mould = & __HZK[((qhWinHorOffset*2;
//偏移后宽度 驱动函数编写步骤:
外围基础准备硬件初始化 IIC初始化,LCD初始化,UPLL时钟初始化(96M, 0V9650系统时钟),GPIO初始化(GPJ),硬件和软件复位摄像头(GPJ12为CAMERARESET,rCIGCTRL)
0V9650寄存器配置
A、读取OV9650厂商ID,验证是否工作
B、复位所有OV9650寄存器
C、配置OV9650寄存器(用规定的二维数组直接配)
摄像头接口初始化
摄像头接口的一些寄存器初始化:rCIGCTRL,rCIWDOFST
rCISRCFMT
rCIPRTRGFMT,rCIPRTAREA
B、设置内存首地址为LCD缓存数组首地址
:rCIPRCLRSA1~4 C、计算水平和垂直缩放比率和位移量,以及主水平、垂直比率
放入以下三个寄存器中rCIPRSCPRERATIO,rCIPRSCPREDST
rCIPRSCCTRL
中断函数开启和指向
控制台菜单的编写与实现
显示视频
rCIPRSCCTRL|=(1<<15);
//预览缩放开启
rCIIMGCPT =(1<<31)|(1<<29);
//预览缩放捕捉使能
截图,定格
rCIPRSCCTRL &=~(1<<15);
//预览缩放关闭 rCIIMGCPT &=~((1<<31)|(1<<29));
//预览缩放捕捉不使能 视频定格后,LCD_BUFFER数组就是图像数据
放大,缩小
调节偏移量HOffset,VOffset,每次改变后重新初始化摄像头接口
四个额外写出需注意的函数:
1、计算主突发长度和剩余突发长度
CalculateBurstSize
2、/计算预缩放比率及移位量
CalculatePrescalerRatioShift
3、中断函数(只清中断,不干别的)
camera_interrupt
4、SCCB总线函数的读写(像IIC)
Rd_SCCB,Wr_SCCB
网卡驱动
DM9000的一些基本概念:
DM9000对外来说只有两个端口——地址口和数据口,地址口用于输入内部寄存器的地址,而数据口则完成对某一寄存器的读写。DM9000的CMD引脚用来区分这两个端口,当CMD引脚为0时,DM9000的数据线上传输的是寄存器地址,当CMD引脚为1时,传输的是读写数据。
我们把DM9000的A8和A9接为高电平,把A4~A7接为低电平,并且把DM9000的AEN接到s3c2440的nGCS4引脚上,则DM9000的端口基址为0x20000300,如果再把DM9000的CMD引脚接到s3c2440的ADDR2引脚上0x20000304(怎么计算?)
查了一下书,是和存储控制器有关系,每个nGCSx对应128M地址空间,8个nGCSx对应1G地址。
nGCS4刚好对应0x20000000开始的地址,但为什么是0x20000300? 再加上IObase:300 如果将 DM9000的CMD引脚接到s3c2440的ADDR2,由于CMD引脚的高低电平决定地址口和数据口,那么,ADDR2为0时,访问的
就是地址口,所以地址口的起始地址为 ARRD2为0的情况,即0x20000000;ADDR2为1时,(LADDR3~LADDR0 = 0100)访问的就是数 据口,所以数据口的地址即 0x20000004。
如果要写入DM9000中的某个寄存器,则先把该寄存器的地址赋予DM_ADDR_PORT,然后再把要写入的数据赋予DM_DATA_PORT即可。读取DM9000中的某个寄存器也类似。
DM9000寄存器介绍在数据手册11页开始:
DM9000内部有0x3FF大小的SRAM用于接受和发送数据缓存。在发送或接收数据包之前,数据是暂存在这个SRAM中的。当需要连续发送或接收数据时,我们需要分别把DM9000寄存器MWCMD或MRCMD赋予数据端口,这样就指定了SRAM中的某个地址,并且在传输完一个数据后,指针会指向SRAM中的下一个地址,从而完成了连续访问数据的目的。
基于ARP协议的DM9000编写步骤:
初始化EINT7中断,设置中断函数入口
因为DM9000的数据中断引脚INT是连接到s3c2440的外部中断7引脚上的 编写好读和写DM9000寄存器函数,用其配置DM9000的寄存器
并使其用中断方式接收网卡数据,查询方式发送数据
(这步挺难的,很多寄存器要配置,对照着Datasheet和别人的程序)
注:DM9000内的寄存器的地址宏定义时,不用管基址,直接按OFFSET定义
编写好DM9000发送和接受函数(位宽为16)
分别把DM9000寄存器MWCMD或MRCMD赋予数据端口
接受数据时要注意,按照规定的格式来编写
定义一个符合ARP协议格式的数组,用于数据传输
将该数组作为发送函数形参发给PC机请求包
编写中断函数,清屏蔽
将接收函数中的数组内容,用串口打印出控制台
SD卡驱动
SD(全名为Secure Digital Memory Card,安全数码卡)
s3c2440集成了SD控制器,可以方便地读写SD、MMC和对SDIO进行操作。可以用SDIO控制器来编,也可用SPI总线来编 SDIO的应用是未来嵌入式系统最重要的接口技术之一,会取代目前GPIO式的SPI接口
具体的CMD命令和协议内容自己看回资料了。
我使用的SD卡为手机的内存卡(不是MMC),型号为SDHC_V20_CARD,支持 PLV2.0协议
主要讲
编写简单的SD读写步骤:
准备工作,编写要使用到一堆东西:
检查SDIO命令发送,接收是否结束函数Chk_CMD_End(int cmd, int be_resp)
使用到的 CMD和ACMD函数(这部分要配置寄存器,要查值,比较难,参考别人的吧)
编写一个要用的结构体SD_STRUCT,用于记录cCardType和iCardRCA等
SD卡识别模式:
初始化:时钟400K,Type B, clk enable,SD卡模式,FIFO reset CMD 0——》reset指令
CMD 8——》工作电压范围,初始化SDHC卡,看是否支持PLV2.0,返回类型 CMD 55——》RCA为0x0,使用ACMD41前必须使用 ACMD 41——》识别卡能否在给定的VDD下工作 CMD 2——》查卡的CID信息
CMD 3——》要求系统给SD卡发送一个新相对地址 RCA 改时钟频率:25M,准备进入transfer状态 CMD 7——》进入 transfer状态 ACMD 6——》设置总线宽度为 4bit
数据传输模式:
若需要擦除: { CMD 32——》start address CMD 33——》end address CMD 38——》erase }注意:擦除后必须要进行复位,即重新初始化前面步骤,不然不能进行读写
单块写
或多块写
CMD 7——》transfer模式
单块读或多块读
拓展:
将摄像头捕捉的图像,写进SD卡中,然后再还原显示出来 注意一个点:
就是要将LCD_BUFFER[480][272]的二维数组转化为LCD_BUFFER_SD[480*272]一维 一个疑问?好像只需 51200就可将整幅图像都显示出来,而不需要480*272那么多
附加部分(有空研究): 文件系统的构建:
12、NORFLASH操作
norflash为EN29LV160AB
对norflash的操作主要就是读、写、擦除和识别
EN29LV160AB的数据宽度可以是8位字节型,也可以是16位的字型,它由某一引脚配置实现的。在这里我们选择字型
BYTE#位置硬件接地,直接默认为Word Mode(16位)
写操作只能使―1‖变为―0‖,而只有擦除才能使―0‖变为―1‖。因此在写之前一定要先擦除 对norflash另一个比较常用的操作是读取芯片的ID
NorFlash启动,nGCS0接NorFlash,起始地址为0x0。0x0000 0000–0x0800 0000为NorFlash
注意几个地方:
1、#define
CMD_ADDR0
*((volatile U16 *)(0x555<<1+flash_base))
之所以又把norflash中的地址向左移一位(即乘以2),是因为我们是把s3c2440的ADDR1连接到了norflash的A0上的缘故,(自己看原理图),容易理解,等于本来最低位的数被推后没了,要左移拉回来。
2、check_toggle函数(校验函数)中
oldtoggle = *((volatile U16 *)0x0);
newtoggle = *((volatile U16 *)0x0);
表示两次读值
if((oldtoggle & 0x40)==(newtoggle & 0x40))
//表示两次的DQ6是否相同 if(newtoggle & 0x20)
//DQ5是否为1
几个常用的函数:
读函数:
read_en29lv160ab(U32 addr)return *((volatile U16 *)(addr));//数据要转为16位
写函数:
en29lv160ab_program(U32 addr, U16 dat)第一个周期是把命令0xAA写入地址为0x555的命令寄存器中,第二个周期是把命令0x55写入地址为0x2AA命令寄存器中,第三个周期是把命令0xA0再写入地址为0x555命令寄存器中,第四个周期为真正地把要写入的数据写入到norflash的地址中。
复位函数:
reset_en29lv160ab(void)
是向任一地址写入复位命令0xF0 擦除函数:
en29lv160ab_sector_erase(U32 section_addr)
第一个周期是把命令0xAA写入地址为0x555的命令寄存器中,第二个周期是把命令0x55写入地址为0x2AA命令寄存器中,第三个周期是把命令0x80再写入地址为0x555命令寄存器中,第四个周期是把命令0xAA写入地址为0x555的命令寄存器中,第五个周期是把命令0x55再写入地址为0x2AA命令寄存器中,第六个周期是把命令0x30写入要擦除块的首地址内
验证函数:
check_toggle()
连续两次读取数据总线上的数据,判断数据总线上的第6位数值(DQ6)是否翻转,如果没有翻转则正确,否则还要判断第5位(DQ5),以确定是否是因为超时而引起的翻转。
读ID函数: get_en29lv160ab_id()第一个周期是把命令0xAA写入地址为0x555的命令寄存器中,第二个周期是把命令0x55写入地址为0x2AA命令寄存器中,第三个周期是把命令0x90再写入地址为0x555命令寄存器中,第四个周期为读取地址为0x100中的内容,即厂商ID(0x1C)。读取设备ID的过程的前三个周期与读取厂商ID相同,第四个周期是读取地址为0x01中的内容,即设备ID(0x2249)
问题1:
原来真的是写得块地址问题,写到前面的0x400就行,写后一点如0xf0000就读不出来了,0x0000 0000–0x0800 0000为NorFlash,为什么后面的不行呢?
问题2:
本来擦除后写完能读出写的内容,但再复位后不写,只读回上次地址,发现读出不同的数据
而且好像擦除后就算返回return 1也没用,擦后读回那地址,也不是全部数据为0的,也是有值的13、NANDFLASH操作
nandflash为K9F2G08U0A,它是8位的nandflash
一个劣势是很容易产生坏块,因此在使用nandflash时,往往要利用校验算法发现坏块并标注出来
K9F2G08U0A的一页为(2K+64)字节(加号前面的2K表示的是main区容量,加号后面的64表示的是spare区容量),它的一块为64页,而整个设备包括了2048个块
内存容量大小计算:
要实现用8个IO口来要访问这么大的容量,K9F2G08U0A规定了用5个周期来实现。
列地址是用于寻址页内空间,行地址用于寻址页,如果要直接访问块,则需要从地址A18开始。
CMD_STATUS读状态命令可以实现读取设备内的状态寄存器,通过该命令可以获知写操作或擦除操作是否完成(判断第6位),以及是否成功完成(判断第0位)。
自己编写NANDFLASH的步骤:(进行块擦除,页写,页读)
1、在头文件中列出相关的固量 和 函数 宏
常用的各种命令集;一些基于NANDFLASH控制器的基本函数
ECC应用和使能nandflash片选;NFSTAT寄存器
一些返回判断值的宏定义
2、写出几个基本的初始化函数:
管脚,寄存器初始化函数 ; 复位函数 ; 读NAND ID号函数
3、相关的检测判断函数:
A、检测是否写擦除成功函数NF_toggle:通过NF_CMD(CMD_STATUS)状态位
B、测试是否坏块函数NF_testBadBlock:读取2054处坏块标志位
C、写坏块标志函数NF_MarkBadBlock:写标志到 2054处
4、读、写、擦除函数:
具体自己看代码了,有两点要注意了:
A、读操作 检测正确性是通过ECC检验码,而写和擦除是通过toggle函数
B、随机读写 是 在读页,写页的基础上 加上列的部分
注意区别两个东西:
1、ECC检验码的判断
读操作来说,我们还要继续读取spare区的相应地址内容,已得到上次写操作时所存储的main区和spare区的ECC,并把这些数据分别放入NFMECCD0/1和NFSECCD的相应位置中。最后我们就可以通过读取NFESTAT0/1(因为K9F2G08U0A是8位IO口,因此这里只用到了NFESTAT0)中的低4位来判断读取的数据是否正确,其中第0位和第1位为main区指示错误,第2位和第3位为spare区指示错误。
2、坏块的判断
对于写页和擦除操作来说,通过toggle函数检测: NF_CMD(CMD_STATUS);
//读状态命令 //判断状态值的第6位是否为1,即是否在忙
//判断状态值的第0位是否为0,为0则写操作正确,否则错误
若第0位操作错误,则该块为 坏块
3、main区和spare区的解锁只为了产生ECC码,一旦产生后,就要锁上,打开另外区的,不要影响互相顺序的ECC码产生
扩展:从NANDFLASH启动程序 当检测到是由nandflash启动时,系统会自动把nandflash中的前4k字节的数据加载到Steppingstone中,然后把该Steppingstone映射为Bank0,因此系统会从Steppingstone开始运行程序,从而实现了s3c2440的nandflash自启动的功能 通过在Steppingstone中,把程序的其余部分复制到RAM中,程序运行时由Steppingstone转移到该RAM中,SRAM起始地址0x30000000
RdNF2SDRAM是C语言编写的一段把nandflash中的数据复制到SRAM的程序
看看这函数,了解一下就行了
附加:nandflash、norflash、SDRAM的区别:
ROM在系统停止供电的时候仍然可以保持数据,RAM通常都是在掉电之后就丢失数据,典型的RAM就是计算机的内存。
SRAM速度非常快,是目前读写最快的存储设备了,但是它也非常昂贵,所以只在要求很苛刻的地方使用,譬如CPU的一级缓冲,二级缓冲 动态RAM(Dynamic RAM/DRAM),DRAM保留数据的时间很短,速度也比SRAM慢,不过它还是比任何的ROM都要快,但从价格上来说DRAM相比SRAM要便宜很多,计算机内存就是DRAM的。
RAM
SRAM
DRAM
SDRAM,DDR RAM等
ROM
EROM,E2PROM等
NANDFLASH、NORFALSH等
NOR和NAND是现在市场上两种主要的非易失闪存技术。Intel于1988年首先开发出NOR flash技术,彻底改变了原先由EPROM和EEPROM一统天下的局面。
NOR比较好操作,因为集成了系统地址线和数据线在芯片内,而NAND只有8个I/O,要操作,还需要结合相应硬件,如2440里有的 NAND控制器。
14、基于OHCI的USB主机(仅做了枚举)发现了一个很好的博客讲这个的:http://lancelot.blog.51cto.com/393579/328233 部分经典内容摘要:
在OHCI规范中,最重要的几个概念是端点(EndPointTD)、主机控制器通信区(HCCA)。其中ED负责确定传输类型(控制传输、批量传输、同步传输和中断传输)。TD确定传输参数。HCCA用于确定数据传输是否完毕。
OHCI(基本流程)
进行控制/批量传输的主要处理流程如下:
1、创建控制/批量传输的ED列表;
2、创建ED下的TD列表;
3、设置命令到相应寄存器开始数据传输;
4、在中断处理程序中判断数据传输是否结束;
在OHCI层,主要完成如下功能:
l 通过控制端口读写数据(包含SETUP、DATA、STATUS等3个TD); l 通过控制端口发送设置命令(没有DATA的TD); l 通过批量端口读数据; l 通过批量端口写数据; l 中断处理程序;
在OHCI的体系下,判断数据是否传输完毕是需要通过中断程序来判断的,当USB主机设置了HcControl和HcCommandStatus寄存器开始传输数据后,AM9200 自动开始数据传输,并且定期的检查HcDoneHead寄存器的内容,并且将其转移到HCCA.DoneHead。然后产生中断,触发中断处理程序。
在中断处理程序中需要判断当前结束的TD是否是当前命令的最后一个TD,这样才能确保整个ED处理完毕。
进行U盘的数据传输时需要通过批量传输端口收发数据,所使用的协议为Mass Storage协议
要学习的一种数据结构与结构体结合应用的模式: 声明时: typedef struct _ED {
………… } ED,*P_ED;
__inline void CreateEd(unsigned int EDAddr ,………….){
P_ED
pED =(P_ED)EDAddr;//这个在函数中创建结构体pED来初始化
pED->Control =…………………….}
调用时,因16字节对齐:
__align(16)EDed;
(声明)CreateEd((unsigned int)&ed, // ED Address
………….);
简单的USB设备枚举,读取描述符:(控制传输步骤)
搞好最重要的3个数据结构:
端点描述符ED,传输描述符TD和共享数据域HCCA
由于HCCA只是创建一个空间域,不需要初始化
而ED和TD在设备枚举时需要不同初始化,故还要创建两个
初始化结构体函数:CreateEd 和CreateGenTd
初始化OCHI寄存器(仅限枚举部分,不涉及中断)
复位,设置帧间隔,初始化HcDoneHead,设置HC为运行状态 写HCCA(开拓一片域)
检测是否有USB设备
设一定的时间检测,如 for(i=0;i<100000;i++)
设备枚举的5个过程:第一步,主机得到设备描述符
第二步 为设备分配地址
第三步,主机用新的地址再次获取设备描述符 第四步,主机读取设备全部配置描述符
第五步,主机发送SETUP数据包,用以设置配置,允许所有端点进入工作状态。
注意:控制写传输需要3个TD:第一个发送Setup包,第二个用于接收握手或零长度的数据包,第三个用于发送状态;
控制读传输需要4个TD:第一个发送Setup包,第二个用于接收数据,第三个用于发送一个零长度的数据包,第四个用于接收状态
具体有两种方法判断TD是否传送完成:
中断法
初始化好中断寄存器rHcInterruptEnable |=(1<<1)|(1<<31);
rHcInterruptStatus |=(1<<1);pISR_USBH =(int)USBH_interrupt;rINTMSK = ~BIT_USBH;当有TD完成时,便进入中断。
如何判断枚举过程中每一步是否最后一个TD完成呢?
可以通过TD创造函数中DelayInterrupt即DI变量设置来巧妙解决
当DI=0x7时,即使TD完成也不会进入中断的,故可以只在每一阶段最后的TD设为非0x7,其余的设为0x7,这样进入中断就代表是最后数据发送完成了
2、状态检测法:
可以通过rHcCommandStatus寄存器中第二位是否1来判断Controllistfilled。
当其为1时,表示还有TD在队列,没发送完成 为 0时,表示无TD在队列,全部发送完
可以这样检测:while(rHcCommandStatus&&0x02)
{
Delay(50);
}
具体的UFI读写设备参考网上的吧(弄了很久,还没弄成功)
1、查询
2、读余量
3、读写扇区
好啦,到这里,算是结束了,开始带LINUX系统的真正学习啦,GO 一切都会有新的开始。。。。。
第三篇:如何开发经销商学习心得
如何开发经销商学习心得
作为渠道销售面临最直接的问题就是如何开发属于自己的经销商客户,如何去选择好的经销商。通过学习“经销商开发”一文,收益很多。
我们在选择经销商时应该确立一个标准,按照标准进行选择。选择要求可以参考西方、美国等国家大学选材思想进行:宽进严出的标准。可以从以下几点进行思考经销商的选择:
1、全面考察评估经销商情况。我们要从经销商的实力(人力、财力、知名度等方面)、市场能力(经销商目前所代理产品销售情况)、管理能力、口碑(在同行中该经销商的信誉度是否够高)、合作意愿度(是否有强烈的合作意愿,对该品牌的市场前景是否有信心)。
2、选择适合自己公司的经销商。我们选择经销商时不一定非要需求大的,知名度非常高的经销商。不可否认,大的经销商无论从经济实力还是市场能力、口碑等方面都强于那些中小型的经销商,一旦抓住了一个大的经销商,就相当于开发了好几个中小型经销商,而且对于公司的产品销量会非常稳定。但是,大的经销商一般都有自己稳定的、长期合作的知名公司的货源,想要从中打破平衡,难度可想而知。我们在选择经销商时要注意从你的整体销售全局考虑,权衡大小,选择适合自己公司销售策略的经销商才是最好的。
3、选择经销商要与企业市场发展策略匹配。选择的经销商其市场销售策略如果不能与公司整体市场发展策略同步,其后果必然会导致市场混乱,对公司自身产品品牌建设有害无益。
那么,我们怎么去了解、判断该经销商的能力呢?我们可以采取多问多观察方式进行。比如我们可以问某个经销商其代理的品牌经营状况,在各种渠道销售情况,以拜访者身份请教当地市场经营情况、消费情况、企业发展状况等,如果经销商对这些问题不知道或者含糊其辞,那么至少说明该经销商实际经营能力较差,由此可以初步判断我们是否继续发展该经销商作为自己的代理商。
除了问经销商问题之外,我们还要在初步选择的经销商店铺门面上进行1周或者1个月的观察,了解该经销商早晚经营情况,人流量是否多、电话是否多、现场管理能力如何等等。了解经销商网络知名度、资金状况、库存状况等。
个人认为,在选择经销商时,合作意愿度是最为重要的,只要经销商合作意愿度强,诚心合作,那么很多问题都可以迎刃而解。对于选择到的中小型经销商,我们可以帮助他们跟我们一起发展,建立长期合作关系,实现双赢局面,这样的合作关系更加牢固长久。
在新的区域,选择新的经销商时,由于对市场了解度不够,选择到了“错误“的经销商时,我们要当机立断,马上考虑对其进行替换,否则可能导致严重的后果。
以上是个人在学习如何开发经销商时初步认识,市场是瞬息万变的,不确定因素、情况很多,只有在实际实践中学些、摸索,找出自己的销售、管理方法,才能在动态市场中立于不败。
第四篇:Hi3516开发学习心得
Hi3516开发学习心得
1.开发环境 1.1>概述
嵌入式系统通常是一个资源受限的系统,因此直接在嵌入式系统的硬件平台上编写软件比较困难,有时候甚至是不可能的。
目前的解决方法是采用宿主机、目标机的开发模式。在宿主机上编写程序,通过交叉编译器生成目标板上可以执行的二进制代码。然后通过文件系统,将二进制代码放在特定目录下,在目标板上运行。
宿主机:VMWARE ubuntu(服务器:192.168.1.101)环境:交叉开发工具包,nfs 目标机:linux hi3516
1.2>安装过程
参见卖方附赠视频教程“SDK安装及编译视频.avi”。nfs文件系统挂载:
挂载点改变修改/etc/exports文件,具体方法自行谷歌。
2.开发流程
首先要有这样一个意识,即对开发板进行的配置,是以我们的需求为基础的。我们所做的工作在系统层次图的 应用层 部分:
所需的函数统一由MPP(媒体处理平台)提供接口。MPP封装实现了基本功能函数,屏蔽掉了对底层的操作。
sample_venc.c为完成视频采集编码一系列操作的代码。观察其代码风格如下:
1.函数返回值作为状态码,用以判断函数是否执行成功。2.函数需要传出的值通过全局变量或地址传出。
现在以函数先后顺序(也即视频采集编码处理流程)对各步骤做以说明。
2.1配置视频缓存池:
视频缓存池作用:视频缓存池主要向媒体业务提供大块物理内存管理功能,负责内存的分配和回收,充分发挥内存缓存池的作用,让物理内存资源在各个媒体处理模块中合理使用。
由于视频输入通道不提供创建和销毁公共视频缓存池功能,因此,在系统初始化之前,必须为视频输入通道配置公共视频缓存池。根据业务的不同,公共缓存池的数量、缓存块的大小和数量会有所不同。
enPayLoad---------------决定编码格式。enSize--------------------决定图像分辨率。(利用不同的编码通道,可以对同一rowdata编码出不同的分辨率。)
缓存块的大小根据 图像制式(PAL/NTSC)、图像分辨率、像素格式、图像跨距(字节对齐)共同决定。函数SAMPLE_COMM_SYS_CalcPicVbBlkSize计算缓存块大小。
2.2初始化MPP系统
由于 MPP系统的正常运行依赖于缓存池,因此必须先调用 HI_MPI_VB_Init 初始 化缓存池,再初始化 MPP系统。
如果多次初始化,仍会返回成功,但实际上系统不会对 MPP的运行状态有任何影 响。函数SAMPLE_COMM_SYS_Init完成MPP系统初始化。
2.3启动VI设备
分为4步: Tips: isp run must at this step--after vi dev enable, before vi chn enable 2.3.1 sensor初始化 2.3.2 启动VI设备 2.3.3 启动ISP 2.3.4 使能VI通道
(函数皆由MPP提供接口)2.4启动视频处理模块
根据 制式和分辨率 获得图像宽和高的具体数据。再由此确定通道属性,进行配置。每一个通道 对应一系列的 硬件和软件功能模块,通道可以进行“串联”,以完成一系列处理。通道的“串联”在开发文档中成为 通道绑定。在整个处理流程中,要先后经过如下的通道绑定:
VI----VPSS VPSS----VENC(后者绑定前者)
2.4.1 启动VPSS 主要根据制式和分辨率 获得图像宽和高的具体数据。再由此确定通道属性,进行配置。然后使能VPSS通道。
2.4.2 VPSS绑定VI 函数由MPP提供,直接调用。
2.5 启动视频流编码
首先创建编码组,再创建编码通道。(编码通道组是指芯片启动一次,能够同时处理的编码通道的集合。通道组模块接收外部原始图像数据,而不关心图像数据是来自哪个
外部模块。我的理解是主通道为物理通道,所有绑定与同一物理通道的扩展通道,其数据源是一样的。关于他们的具体关系,参见开发参考文档的“6 视频编码模块------6.2.2 编码通道与通道组”。)然后将编码通道注册到通道组。接着开启编码通道接收输入图像。
2.6 获取编码码流
在这里新开了一个线程专门用于接收码流。具体思路为:
把编码通道映射为一个文件句柄(函数HI_MPI_VENC_GetFd完成此操作)。接着的所有操作都是对文件句柄进行的。这完全类似于端口通信中将端口打开视作文件句柄,然后进行读写操作。
流程图如下:
>1.获取通道的文件句柄。>2.从各通道获取码流。(这里一共有3<012>个通道,分别产生分辨率为1080p,VGA,QVGA的h264编码码流。--------------我们的工程中只需要编码通道0的1080p码流)
>2.1 查询编码码流的一帧中有多少的包。>2.2 根据包的数目分配内存。>2.3获取一帧数据。
>2.4 根据编码类型存数据。>2.5 释放流。(不及时释放,当视频缓冲池满时即停止编码)>2.6 释放分配的内存。>2.7 关闭文件句柄。>2.8 循环执行以上7步。
3.附录
3.1 名词解释
视频输入(VI)、视频处理(VPSS)、视频编码(VENC)、视频解码(VDEC)、视频输出(VO)、视频侦测分析(VDA)、音频输入(AI)、音频输出(AO)、音频编码(AENC)、音频解码(ADEC)、编码通道组子模块(GROUP)区域管理(REGION)
视频编码层(VCL, VideoCoding Layer)
网络提取层(NAL, NetworkAbstraction Layer)
SDK
Software Developer’s Kit
软件开发工具包 MPP
Media Processing Platform
媒体处理平台 AI
Auto Iris Control
自动光圈控制 VEDU
Video Encode Unit
视频编码单元 IVE
Intelligent Video Engineering
智能加速引擎 PTS
Presentation Time Stamp
时间戳
DDRC
Double-Data Rate Controller
DDR 控制器 MMZ
Media Memory Zone
媒体内存区域 ROI
Region Of Interest
感兴趣区域
3.2 其他要点
1.当输入图像大小与编码通道宽高完全匹配时,才能启动编码通道进行编码。2.未尽事宜,参阅开发完档。
第五篇:培训与开发学习心得
《培训与开发》学习心得
通过半年时间对《培训与开发》的学习,对培训与开发相关方面的知识有所掌握。培训的一个重要职能是促进学习,它通过周密的组织和安排来帮助员工发现和获得所需的知识和能力,使他们更好地完成本职工作。
员工培训实际上是被培训人员学习的过程。对于新进公司的员工来说,要尽快适应并胜任工作,除了自己努力学习,还需要公司提供帮助。对于在岗的员工来说,为了适应市场形势的变化带来的公司战略的调整,需要不断调整和提高自己的技能。基于这两个方面,组织有效培训,以最大限度开发员工的潜能变得非常必要。就内容而言,培训工作有企业文化培训,规章制度培训,岗位技能培训以及管理技能开发培训。培训工作必须做到具有针对性,要考虑不同受训者群体的具体需求。对于新进员工来说,培训工作能够帮助他们适应并胜任工作,对于在岗员工来说,培训能够帮助他们掌握岗位所需要的新技能,并帮助他们最大限度开发自己的潜能,而对于公司来说,培训工作会让企业工作顺利开展,业绩不断提高。
对于新员工,应进行基础教育,包括:公司概况、规章制度、方针目标、安全教育、相关法律法规及标准基础知识等培训内容。一是岗前技能培训。对于新进或换岗人员,应进行岗前技能培训,包括:相关作业文件、相关设备性能与操作规程、操作技能、安全注意事项及紧急情况下的应变措施等培训内容,由所在岗位部门负责人组织并运行书面和操作考核。对于政府劳动部门规定必须持证上岗的特种作业人员,必须到指定的机
构进行培训。二是在职培训。对于新产品、项目或合同或工艺技术发生变更,导致相关岗位工作内容与要求,发生变化时,应由所在岗位的主管随时组织培训,培训后应以适当方式进行考核。在日常工作中暴露出的员工工作技能,达不到规定要求的,应由所在岗位的直属主管随时组织培训,并在培训后,以适当方式进行考核。
质量意识教育。通过上岗基础教育及岗前技能培训,必须逐步培养员工的质量意识,使他们意识到他们活动的相关性和重要性,以及他们如何为达到质量目标做出贡献,同时也应强调满足顾客要求和期望的重要性,以及未能满足要求所造成的后果。
培训效果评估。从员工质量意识提高、工作能力的提升、工作绩效的改进等方面评估培训效果,为以后培训工作的计划、组织与实施提供改进意见和建议。