曾经,会单片机的工程师牛得一塌糊涂。想十年前一个会单片机的工程师几乎就是嵌入式工程师的代名词。
若干年前,ARM开始暂露头角,看到单片机工程师的牛气,ARM工程师笑了。而从包括合众达在内的中国DSP三巨头开始在中国推广DSP时,所有开始使用DSP的工程师笑了。他们有理由笑,他们有资格笑。因为在那时,DSP就代表着高高在上,收入高、职位高、声誉高,典型的三高。而经过若干年的推广,DSP已经脱下了神的外衣,走下了神坛。会DSP的人越来越多。但随着DSP开发者的日渐增多,DSP的娘嫁人(TI)发现,纯DSP血统的姑娘们越来越难嫁了。时代的青年对于姑娘的要求已经不再在能做一手漂亮而高效的女红(计算)。人们希望娶到家的姑娘是出得厅堂,进得厨房,能歌而善舞。大户人家的公子希望媳妇儿如DSP般贤良淑德,又像ARM般千妖百媚。
2005年TI推出了DaVinci技术,这一血统的姑娘既贤良淑德又千娇百媚。ARM926 + 64x+在世界各地的选美比赛中,DaVinci小姐一路过关斩将,一届又一届地当选为世界小姐。但后来人们发现,所有的评委都是对AV比较感兴趣的。一时间,AV门事件波及全球。在人民大众强大的呼声里,OMAPL小姐,姗姗来迟。 ARM+674x(定浮点DSP)。她是如此的大方美丽,如此的平易近人,她是无冕的后冠。
接下来的几天,我会继续介续OMAPL处理器家族。在我做完基本介绍之后,我的同事even会讲述如何实现ARM+DSP的通信。
1 器件功能组成
名词解释:
OMAPL = Oh My Application Processor Low-power edition. (Blacksword独家解释)
![](https://i-blog.csdnimg.cn/blog_migrate/5a081f58bf08065c4c761703f0334227.jpeg)
OMAPL处理器内部构成:
介绍OMAPL内部构成之前,我们先来回顾一下TI的DSP功能结构。
下图是TMS320C6748的blockdiagram
![](https://i-blog.csdnimg.cn/blog_migrate/65a1574974b83303330682a05ef92bb1.jpeg)
从图上可以看出DSP器件其实本质上就是一个DSP运算核心,通过Switch Fabric/EDMA连接了一堆片上外设而已。至于核心那部分,我们大部分只是DSP器件的使用者,而不是设计者,不需要花过多的精力去深究。
我们以前讲DSP的开发:就硬件而言即将需要用到的片内外设引出来而已,把片外的外设连接到总线上而已;而做硬件,我个人认为都无所谓是否DSP工程师,因为DSP也好、ARM也好、X86也好,考验工程师的都是指定的板子硬件线路连接正确性,能不能在指定面积上布完,电路会否出现局部过热,电磁兼容性好否,高速接口线长线宽是否合理等,而这一切不会因是否DSP而有任何的不同。
个人认就DSP而言,软件的开发,才是真正的DSP开发。而就软件而言,即设置好SwitchFabric以便能够选中指定的外设,然后读取指定外设上的数据,将这此处理好的数据再写到其它指定的外设上而已。从这一点上讲,开发DSP本身并非高高在上的神话。大部分所谓的DSP高手,其实严格来讲应该说是数学高手,逻辑高手,他们小小的一点手段,就可以让算法效率提高很多。真正的高手,只有实在在算法没什么可以抠的,才会使用汇编。
那我们不管高手不高手的,总之其实要会写一个简单的DSP程序,做基本的处理,大家都觉得比较容易的。
在第0讲中,我们提到TI从2005年推出了DaVinci系列平台。但很多人用了后,心里有着说不出的委屈,尤其是少部分因为DaVinci而被减员下来的。
这部分人看到了OMAP-L,觉得OMAP-L这个平台非常“亲切”,“亲切”得让他们牙痒痒。怎么看怎么像DaVinci。且放下“亲切”的问题不谈,我们先来看看DaVinci的表妹OMAP-L到底长得什么样。盖头掀开,OMAP-L的脸蛋身材如下图(图为OMAP-L138):
![](https://i-blog.csdnimg.cn/blog_migrate/e1dc4713e354d0f4b700a6332bebba06.jpeg)
与表姐DaVinci相好过的人,一定能看出来。表姐表妹的区别仅在于一个胸大(在VICP),一个秀气(DSP为定浮点)
但我更希望大家将目光从胸部移开,这样才有助于我们从整体上认识DaVinci与OMAP-L这对姐妹花。
请大家看看OMAP-L138与TMS320C6748(代表了传统DSP)之间的联系与区别。你一定会很容易就发现:
共同点就是同样是处理器核心通过Switch连接到各种不同的片上外设。
而最大的不同点就是OMAP-L片内有两个处理器核心,一个ARM 一个DSP。
你要是问一下有经验的DSP开发工程师,开发DSP难不难,你会得到什么答案?
同样你可以问一下有经验的ARM开发工程,开发ARM难不难,你会得到什么答案?
很多公司在很多项目中已经同时使用ARM和DSP,那怎么将ARM和DSP混搭出来的DaVinci/OMAP-L怎么就有很多人觉得不好用呢?
其实这个问题诚然有TI的原因,但与我们本身的用法也有很大的关系。觉得他不好用、不美是很正常的。不信:
你去问一下有经验的DSP工程师,ARM开发容易否?
很多早期开发DaVinci的公司,一个像样的ARM工程师都拿不出来,然后就在那里叫嚷TI提供的东西不全,DaVinci的架构不好,到今天他们也还在说OMAP-L架构不好,就是看着OMAP-L看着像DaVinci。
我们承认对于你的应用TI提供的软件可能相当不全。但这正在DaVinci的魅力所在,毕竟DaVinci提供的不是山寨货,而是提供给大家实现无限创意的能力。
那么在基本组件方面,TI会致力于提供给大家符合Linux标准的各种驱动及软件中间件。有了标准的保证,你会发现如GUI或是RTP/RTSP等更上一个层次的软件组件上,你根本就不缺软件,因为大量的开源项目都是你的项目。
我曾经有一个移植Gnash(Linux下的Flash播放器)的惨痛教训,在TI DaVinci平台仅花了几天时间,所有软件就移植成功。D1以下的基本上能达到15FPS。
但在另一个厂商所谓完善的平台,确认有对于某几个特定应用的完整方案,几乎可以直接将代码用于量产。但当客户需要Flash时,找到了我。我遇到的第一个问题是该平台提供的C语言库是不完整的,不得已我给客户重新移植了C语言库以及编译器。我们都知道在嵌入式产品上要显示就通常会用到Framebuffer。我的第二个问题,就是Gnash要用到SDL,SDL最轻量的backend就是framebuffer。但我无比痛苦的发现,该平台上的framebuffer的驱动并不标准......
做了无数的修改之后,终于将Gnash在客户的平台上运行起来,新的问题是该平台提供的那些完整解决方案,不能运行在新的C库上,然后是非常痛苦的改“解决方案”中程序的过程,总共浪费了好几个月。
因此,我们认为TI的平台还是比较容易使用的,关键是你得让合适的人干合适的事情。后面我们会分析这个架构,并讲述基本的开发流程。
下面我们看一下OMAP-L处理器家族都有哪些成员及旁系。
OMAPL处理器家族成员:
型 号 | 主频(MHz) | PWM | DMA | CPU | RAM | L1 | L2 | 封装 | McASP | McBSP | MMC/SD | Timer(64bit) | uPP | PRU | 其它 |
OMAPL137 | 375, 456 | 3 | 32ch EDMA | ARM9, 674x | SDRAM | 64K | 256K | 256BGA | 3 | 1 | 1GP, 1GP/WD | UART, USB, LCD, I2C, HPI, eCAP, EMAC, SATA | |||
OMAPL138 | 375, 456 | 2 | 2 32ch EDMA | ARM9, 674x | SDRAM, DDR/2 | 64K | 256K | 361 NFBGA | 1 | 2 | 2 | 3GP, 1GP/WD | 1 | 2 | UART, USB, LCD, I2C, HPI, eCAP, EMAC, SATA |
OMAPL旁系之ARM成员:
AM1808 OMAPL138的无DSP版本
AM1806 AM1808的无EMAC及SATA版本
AM1707 OMAPL137的无DSP版本
OMAPL旁系之DSP成员:
TMS320C6748 OMAPL138的不带ARM版本
TMS320C6746 TMS320C6748阉EMAC及SATA后的版本
TMS320C6742 阉得就更多了
TMS320C6747 OMAPL138的阉DSP版本
暂时讲到这里,稍候继续
2 SCR Switch Central Resource介绍
关于ARM的核心,由于我们不再介绍,它不会因为披上了TI的外衣就不是ARM
关于DSP的核心本身,我们也不再介绍,毕竟,这里我们不是要讲如何做算法的优化。因此流水线与指令集,都不在考虑之内。
就一个应用而言,最主要的就是各种外设的使用。
而对外设的使用而言,最重要的就是这堆玩意儿是怎么连接到CPU上的。
如我们在《OMAP-L处理器介绍 1》中所讲,在OMAP-L处理器上,外设与CPU们是通过SCR连接在一起的。
好,废话少说。上图先!
图1 OMAP-L138 Switched Central Resource
看到这个图,肯定很多人与我最开始的感觉一样,什么乱七八糟的呀。
里面的SCR还好说,肯定是Switch Central Resource
但BR是什么呢?TI没有给出缩略语说明,但在SPRUGM7D那篇名为《OMAP-L138 Application Processor System Referrence Guide》第4.1节,也就是上面这个图的上面一段文字里列到"The switch fabric is composed of multiple switched central resources (SCRs) and multiple bridges"。
显然这里的 BR就是BRidge的意思。
这样这个图的作用,就显然了,就是说数据可以从哪里到哪里。
而在这个算是器里面有能力决定哪个使用哪个SCR或BR的核心为ARM, DSP, EDMA以及PRU.
讲到这可能已经有很多朋友在骂我罗嗦了。
“那么到底该怎么决定哪个SCR甚至哪一个BR被使用才能访问到一个外设, 比如说串口?你要不说清楚,今天别想走!”(腹黑中)
“我们TI,把所有的这些挂在SCR上的外设,统一进行了编址?每个设备都对应在特定的地址上,比如说串口0, 0x01c4 2000 ——0x01c4 2034分别对应着串口寄存器RDR-MDR。客户只需要大CPU上访问0x01c4 2000就可以访问相应串口寄存器,对应的SCR及BR会自动选上” (想知道我们TI是怎么设计器件,嘿嘿嘿!)
被忽悠了半天看这个图,比较不爽!(权当消遣了,学习一下芯片的实现原理)
对于我们要用好器件上的外设而言,比较实惠的还是memoryMap。 下图为OMAPL138器件的MemoryMap(摘自OmapL138的datasheet).
![](https://i-blog.csdnimg.cn/blog_migrate/c0003fd024647da74e2307ac09d8ff53.jpeg)
图2 OMAPL138 MemoryMap
从数据表我们可以看出如果要从DSP访问DSPL2RAM,只要在DSP代码里访问0x0080,0000到0x0083,ffff
有的人可能要问题,要是我候通过ARM访问呢,那我们往下翻MemoryMap这个表(如下图)
![](https://i-blog.csdnimg.cn/blog_migrate/5478fea3ce2e9e83021a759963675f38.jpeg)
图3 OMAPL138 MemoryMap Continued
只需要在ARM里访问物理地址0x1180,0000—0x1183,ffff (当然是指物理地址,而不是操作系统内核地址或是用户空间地址)。
前面说了各个处理器怎么看外设,尽管图一中也能看出ARM与DSP关系的一点端倪,但估计还有很多人在疑惑ARM与DSP这小两口怎么是什么关系。(废话,你都说是小两口了)。
我们总结一下图1及图2,3所涉及到的MemoryMap表的的内容,我们就不难看现,其实ARM与DSP之间的关系就是如图4这样
![](https://i-blog.csdnimg.cn/blog_migrate/251c41acc93a0e935f02eb29d35a035e.jpeg)
图4 站在民政局的角度打量ARM与DSP的关系。
今天我们知道了ARM及DSP怎么通过SCR访问的外设,也知ARM与DSP是怎么XXOO的。
关于ARM与DSP XXOO的详细介绍,我们在下一讲介绍。
敬请期待。
3 以SEED-DIM138/1808-Kit上的LED灯D1为例讲述如何进行相关开发(一)
本文档的使用许可证:
本文档遵循GPLv2发布,因此任何人引用或修改本文档均需要在发起引用的文章的显要位置(开头或结束处)原样引入本段文字(互联网及电子文档为斜体蓝色,)及所引用版本的下一段的文档编辑人员的列表及工作内容。
适用器件:操作适用于基于TI OMAP-Lx8 C6748 AM18xx处理器的SEED-DIM138/1808-kit,方法适用于TI Sitara、DaVinci、OMAP、Integra、C67xx等系列的绝大多数处理器。 原理适用于所有嵌入式处理器上的产品开发。
嵌入式的开发与PC程序不同,通常需要与定制的开发板或是产品打交道,这就意味着与PC那样的标准架构不同,所有的东西你不能依靠猜来进行。因此我们的工作要从实际硬件出发。那么本文中我们将以SEED-DIM138/1808-Kit上的LED灯D1为例讲述如何进行相关开发,也就是嵌入式的软件开发人员如何从软件的角度看硬件。
一、看原理图
看原理图,是为一款嵌入式产品开发软件(纯应用层软件工程师除外)的必备技能之一。具体如何看原理图,今天我们不在这里讲述。我们看SEED-DIM138底板的原理图,知道灯D1是连接到GP6[13]的,所图1所示。
![](https://i-blog.csdnimg.cn/blog_migrate/6758e9b2188f3b4b1e79687189cf71c8.jpeg)
图1 SEED-DIM138-Kit套件中底板用户LED原理图
由这个图可以看出要想让灯D2发光,必须置GP6_13为高电平。
二、查相关器件手册,了解如何进行配置
从软件工程师的角度,所有的外设都是通过地址来进行访问或配置的。
查一下TI的芯片手册,我们发现Datasheet SPRS586x中第3章是Device Overview,其中会有一节讲述Pin Assignments,那么在这边手册中是3.7节。如图 2 所示的 R 行 17 列是 GP6[13] 所在的引脚,该引脚是一个 GP6[13] 及PRU0_R30[31]等功能的一个复用引脚。(我们知道TI提供的产品功能都比较强大,因此引脚的复用程度都比较大)。
![](http://bbs.eeworld.com.cn/images/default/attachimg.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/713aa94afa2f93f5ad1873433663e355.jpeg)
因此我们首先要解决的是软件的管脚复用问题。我们发现芯片手册SPRS586x中的第3.7节是讲述Pin Multiplexing Control的。"Device level pin multiplexing is controlled by registers PINMUX0 - PINMUX19 in the SYSCFG module."
根据这句话,我们可以在文档里搜索PINMUX或SYSCFG的其它章节,发现本文档中并无进一步详细信息。
因此我们需要进一步看文档 SPRUGM7x ,也就是《 OMAP-L138 Applications Processor System Reference Guide》这篇文档。 SPRUGM7x的第11章是专门讲述SYSCFG(System ConFiG)。其子节11.5.10是详细讲述PINMUX的。我们在里面找到关于GP6[13]的配置说明如图3所示。
![](https://i-blog.csdnimg.cn/blog_migrate/83d264b39e5dcc24cf7f94cf413c12a0.jpeg)
意思是说PINMUX13寄存器的11_8位设为8h即可以令该引脚工作为GPIO的GP6[13]. 那么从软件工程师的角度就是需要知道PINMUX13寄存器的地址。
而同样这11.5节的刚开始就是重点介绍SYSCFG寄存器的,在这节的表里面我们发瑞PINMUX13寄存器对应的地址为01c1 4154h.
![](https://i-blog.csdnimg.cn/blog_migrate/588e3a161e36919f8a3b729972507fd0.jpeg)
现在这个引脚已经是GPIO了。
然后我们就应该读具体外设的手册,那么本外设为GPIO,从TI网站OMAPL138 产品信息页 面查找并 下载到 GPIO 的用户手册 SPRUF18x 《TMS320C674x/OMAP-L1x Processor General-Purpose Input/Output 》,根据这篇文档我们知道要使用这个GPIO引脚我们需要将该GPIO引脚的具体功能设为输出,并向该引脚输出高电平,灯D1才会亮。
而GP6对应的输入还是输出是由DIR67寄存器的GP6P13位也是本寄存的比特13控制的。
![](https://i-blog.csdnimg.cn/blog_migrate/0094029b53acbfe04dff87f5e926ef70.jpeg)
而且,写0表示输出。
![](http://bbs.eeworld.com.cn/images/default/attachimg.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6298981274e8a4a42d0ed9dc286ea510.jpeg)
而往该引脚输出高电平可以通过OUT_DATA67的GP6P13即比特13完成。
![](http://bbs.eeworld.com.cn/images/default/attachimg.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/fb5109a71f09484e32a9f4ea3849840b.jpeg)
因此现在的情况是我们需要知道DIR67及OUT_DATA67的地址。而文档SPRUF18x给出的GPIO寄存器地址对DIR67及OUT_DATA67的分配如图
![](http://bbs.eeworld.com.cn/images/default/attachimg.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1aac06c970e8ddfb3a89b8bc9b5c124c.jpeg)
表7 GPIO寄存的偏移地址分配表
而这里的值是相对值,需要对照芯片的SPRS586x SPRS586x关于Memory Map的分配,发现GPIO地址起始为01e2 6000h.
![](http://bbs.eeworld.com.cn/images/default/attachimg.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b135b5df7cf515afad9f72297f50f3c7.jpeg)
表8 OMAPL1x8器件的Memory Map
[总结一下],看文档的顺序,Datasheet->System Reference Guide->专项手册。 这也是看TI文档的基本套路。
[总结一下],点亮SEED-DIM138/1808-Kit上灯D1的工作流程为:
1 设PINMUX13_11_8 为08h
2 设GPIO DIR67的GP6P13位为0,配置管脚为输出
3 设GPIO OUT_DATA67的GP6P13位为1
下几节我们将实际通过操作CCS而不编程、裸机程序、Linux程序三种手段来以上面介绍的方法来实际点亮这个灯。
三、CCS下简单几步操作点亮灯D1
打开CCS(3.3.82或更新版本,CCSv4.x或后续其它CCS产品), 具体仿真器的配置情况,这里我们不再描述。推荐使用XDS560系列仿真器,如我们合众达的SEED-XDS560Plus因为它确实能让你非常畅地完成我下面要进行的操作。
打开CCS后,选择连接器件的ARM端(DIM138/1808适用)或是DSP端(DIM138/6748适用)。注:需要用到相应的GEL文件。
在View菜单里选择Expressions,这是一个我们通过CCS调试程序时监测变量的一个窗口。如图3所示。
![](http://bbs.eeworld.com.cn/images/default/attachimg.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/d5e411afce5880a2428bcde93dfc39bf.jpeg)
点击其中的”Add new expresion”并输入*0x01c14154作为Expression,然后在Value处入0x8<<8。然后再分别加入:
Expression: *0x01e26088 Value: 0xfffffff&(~(0x1<<13))
Expression: *0x01e2608c Value: 0x0|(0x1<<13)
最后输入完的CCS Expressions窗口如图4所示。
![](http://bbs.eeworld.com.cn/images/default/attachimg.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b8b3b517552979d4510d3171177fcaee.jpeg)
到这一步为止,你会发现板子上的LED D1已经亮了。当然在CCS下还可以使用
Memory窗口来完成同样的工作。但我个人觉得Expressions窗口最好用。
四、裸机编程的方法点亮LED D1
程序代码如下:
引用:
#define PINMUX_BASE _ADDR 0x01c14120
#define PINMUX_4_LED_D1 13
#define PINMUX_START_BIT_4_LED_D1 8
#define PINMUX_SET_4_LED_D1 0x8
#define GPIO_BASE_ADDR 0x01e26000
#define GP_BANK_LED1_D1 6
#define GP_PIN_4_LED1_D1 13
typedef enum {PIN_OUTPUT, PIN_INPUT} gpio_DIR;
typedef struct ledStruct
{
unsigned char pinmuxGroup;
unsigned char pinmuxStartBit;
unsigned char pinmuxSet;
unsigned char gpioBank;
unsigned char gpioPin;
bool state; // On or Off
};
bool pinmux(unsigned short pinMuxGroupNum, unsigned short
pinMuxStartBit, unsigned char pinMuxCmd)
{
unsigned int *pinmux = (unsigned int*)( PINMUX_BASE _ADDR +
pinMuxGroupNum *4);
*pinmux = (*pinmux & ~(0xf << pinMuxStartBit)) | (pinMuxCmd <<
pinMuxStartBit);
if(*pinmux & (0xf << pinMuxStartBit )) != (pinMuxCmd <<
pinMuxStartBit)
return false;
else
return true;
}
bool gpioSetDir(unsigned short gpioBank, unsigned char
gpioBankPinNum, gpio_DIR gpioDir )
{
unsigned int *gpioGroup = (unsigned int*) (GPIO_BASE_ADDR +
(gpioBank/2)*4 );
*gpioGroup = ( *gpioGroup & ~(0x1<
( gpioDir << gpioBankPinNum);
if ( *gpioGroup & (0x1<<<
gpioBankPinNum)
return false;
else
return true;
}
bool led_init(ledStruct led)
{
if (!pinmux(led.pinmuxGroup, led.pinmuxStartBit, led.pinmuxSet ))
return false;
if (!gpioSetDir(led.gpioBank, led.gpioPin, PIN_OUTPUT))
return false;
return true;
}
bool led_on(ledStruct led)
{
//TODO: Left for you to implement this function;
}
bool led_off(ledStruct led)
{
//TODO: Left for you to implement this function;
}
!
!
int!main(int!*argc,!char!**argv)!
{!
! //TODO:!Code!to!turn!on!and!swich!off!the!LED!is!left!for!you.!
}!
!
请自行建立 CCS 工程编译本程序,或使用 arm-linux-gcc 来编译本代码。区别是ccs 编译出来的程序可以通过 CCS 来下载执行,arm-linux-gcc 编译出来的版本,可能需要 uboot 或是其它手段才能装载执行。不过 TI 未来推出的 CCS 版本应该会支持 arm-linux-gcc 编译出来的代码(我已经在测试版本 CCS 中见到这一功能)。!
五、Linux下,访问 LED(D1)
Linux下,地址空间分为三种,分别是
用户空间:应用程序代码所能直接访问的地址空间;
内核空间:操作系统内核及驱动程序所能直接访问的地址空间;
物理空间:器件及系统上各种实际的物理地址空间。
从前面的介绍我们知道,从嵌入式开发工程师的角度,各种外设是需要通过物理地址来进行访问的,因此说代码需要跟物理地址发生关系。 而按照 Linux 对地址空间的三种划分,应用程序甚至驱动程序都是不能直接访问的。那么怎么样才能访问物理地址空间呢?Linux 的做法是通过几个桥梁函数架设了驱动程序访问物理地址及应用程序访问物理地址的桥梁。
这些函数分别是: ioremap 函数家族及 remap_pfn_range 函数家族。最后形成的关系如图 5 所示。注意:这两个函数家族均需要在内核空间执行,只是执行后,内核空间及用户空间便可以通过相应的指针来映身到指定的物理地址上,从而实现对物理空间访问。
从前面的介绍我们知道,从嵌入式开发工程师的角度,各种外设是需要通过物理地址来进行访问的,因此说代码需要跟物理地址发生关系。 而按照 Linux 对地址空间的三种划分,应用程序甚至驱动程序都是不能直接访问的。那么怎么样才能访问物理地址空间呢?Linux 的做法是通过几个桥梁函数架设了驱动程序访问物理地址及应用程序访问物理地址的桥梁。
这些函数分别是: ioremap 函数家族及 remap_pfn_range 函数家族。最后形成的关系如图 5 所示。注意:这两个函数家族均需要在内核空间执行,只是执行后,内核空间及用户空间便可以通过相应的指针来映身到指定的物理地址上,从而实现对物理空间访问。
对于单个寄存器,还可以使用 phys_to_virt 函数来获得物理地址对应的内核空间地址,但对于连续空间的访问不建议使用这种方法。因为这不能保证你获得一个连续的内核空间地址区,甚至当你用指针进行操作时,有可能出现内存越界。比如说,内核中地址A及B是连续的两个地址,a 和 b 为连续的两个物理地址,如果你用 phys_to_virt 得到 a 对应的虚拟地址 A,当你需要访问 b 时,你会很自然的使用指针 A+1 也就是B来访问。但这里不幸地是B事先已经被对应用到物理地址X了,而对 X 的野蛮操作引发的灾难,没有人知道。
而内核空间与用户空间要拷贝数据,有函数 copy_from_user 及copy_to_user 可用。
那么仅剩下的问题就是如何写一个字符型的设备驱动程序,以及如何在应用程序里访问驱动程序。
关于如果写字符型设备驱动程序,这里我就不在缀述,几乎每一本讲述Linux 驱动程序的书中都有长篇大论的阐述。(我会在后续给出一个实作的代码,有需要的可以给我写邮件,但由于近阶段一直较忙,白天忙工作,晚上有时要加班,还要给孩子讲故事,这个实作代码可能会比较晚才给出来。写本文时一夜未眠,请见谅。)
4 用常规的CCS开发方式建立一个完整的LED项目
大家好,我是Even,北京合众达电子的产品FAE。对于OMAP-L138这个平台我也是刚接触不久,略懂皮毛。有说的不对的地方还请大家多多指教,一起学习。
之前David已经为大家介绍了CCSV4的使用方法和调试技巧。并通过阅读相关DATASHEET实现用配置寄存器的方式点亮板卡上的LED灯。下面我将用常规的CCS开发方式建立一个完整的LED项目。
对于开发的认识,软件工程师和硬件工程师会有不同的看法。硬件工程师乐于用直接控制硬件的方式来实现系统的功能,如控制相关寄存器的值。这样做的好处是节省系统资源,运行速度快,但需要对各种硬件的寄存器有相当的了解。缺点是遇到大型系统构建时会有些力不从心。软件工程师更乐于基于操作系统进行开发,通过系统调用的方式控制硬件。好处是可以尽量少的接触底层硬件,专心软件的设计。缺点是如果遇到驱动不完善或者有BUG的情况下,就会有些束手无策。精通软硬件是每个嵌入式工程师的最高追求,不过由于专业不同就会有所偏好,找到一个适合自身的开发模式还是十分必要的。
之前说了不少闲话,现在我们言归正题。
OMAP-L138是一个双核的芯片,无论是ARM核还是DSP核,都有相当数量的寄存器需要配置,如果从头开始逐一定义和设置,需要消耗大量的时间,但这又是不得不做的事。庆幸的是TI的第三方已经为我们写好芯片的寄存器定义,并把初始化和功能实现通过C函数的方式集成起来,这样我们只需要调用相关的函数,就可以实现对指定外设初始化工作和使用。
这些文件的集合体被称作BSL(Board Support Library)。它大大减少了开发初期的工作量,注释也全面,容易理解。
下面通过调用BSL的方式,建立一个ARM端点亮LED的项目,
void main()
{
#if NO_GEL
EVMOMAPL138_init(); //初始化芯片,PLL等
EVMOMAPL138_initRAM(); //初始化RAM,DDR
EVMOMAPL138_enableDsp(); //使能DSP
#endif
USTIMER_init(); //初始化CPU定时器
// EVMOMAPL138_pinmuxConfig(PINMUX_GPIO613_REG, PINMUX_GPIO613_MASK, PINMUX_GPIO613_VAL); //设置管脚复用,作为GPIO
EVMOMAPL138_pinmuxConfig(PINMUX_GPIO810_REG, PINMUX_GPIO810_MASK, PINMUX_GPIO810_VAL); //设置管脚复用,作为GPIO
// GPIO_setDir(GPIO_BANK6, GPIO_PIN13, GPIO_OUTPUT); //设置GPIO为输出模式
GPIO_setDir(GPIO_BANK8, GPIO_PIN10, GPIO_OUTPUT); //设置GPIO为输出模式
printf("\r\n\r\n********** OMAP-L138 LED TEST **********\r\n\r\n"); //输出打印信息
while(1)
{
// GPIO_setOutput(GPIO_BANK6, GPIO_PIN13, OUTPUT_LOW);
// USTIMER_delay(500000);
// GPIO_setOutput(GPIO_BANK6, GPIO_PIN13, OUTPUT_HIGH);
// USTIMER_delay(500000);
GPIO_setOutput(GPIO_BANK8, GPIO_PIN10, OUTPUT_LOW); //设置GPIO输出低电平
USTIMER_delay(500000); //延时500ms
GPIO_setOutput(GPIO_BANK8, GPIO_PIN10, OUTPUT_HIGH); //设置GPIO输出高电平
USTIMER_delay(500000); //延时500ms
}
}
上面的代码就是主函数的内容,简洁明了,一看注释就能明白函数的作用。整体思路也是完全按照芯片手册中对外设配置的步骤进行操作的。
1. 开启外设时钟。
2. 设置芯片管脚复用,用为GPIO。
3. 设置GPIO为输出模式。
4. 设置GPIO输出值,高电平或者低电平。
其中第一步是通过EVMOMAPL138_init()这个函数实现的,具体原理可以参考BSL相关的源码注释,此时已经开启了芯片上所有外设时钟,当然也包括了GPIO的。
第二步是由EVMOMAPL138_pinmuxConfig()函数实现的,其内部源码也是对寄存器的操作。
第三部是通过GPIO_setDir()函数实现,可以配置为输入或者输出。当作为输入时,配置会更复杂一些,因为会涉及到中断等内容。
第四步通过GPIO_setOutput()函数实现,给指定的GPIO管脚赋值,输出高电平或者低电平。
由此可见,BSL中大部分函数功能都可以通过名称知道,内部代码注释也很全面,容易理解。这个程序的源码已经打包,在CCSV4中直接导入项目就可以运行。运行结果就是LED灯不停闪烁,亮0.5秒,灭0.5秒。
或许有人会问,为什么除main.c之外,还有很多看上去没用的源文件。这是因为模块的相关性导致的,调用一个模块的功能函数的时候,或许会用到其它模块的函数,因此在编译的时候必须要将其包含进来,否则编译会出错。
无论接触哪种新的硬件平台,点亮LED一般会是工程师第一个要做的实验,就像编写软件HelloWorld一样经典。虽然程序很短小,但集成了系统设计中大部分操作。是新手入门,老手熟悉平台的绝佳选择。
6、DIM138 nand AIS文件烧写说明
各位好,由于最近工作比较忙,很久才更新这次调试笔记,先说声抱歉了。
另外声明一下,下面这篇关于NANDFLASH烧写的文档是我们研发工程师的功劳,我代他发到论坛上来,可不想抢别人的功劳,哈。同时感谢他白忙之中配合此次论坛活动。他调试的时候使用的是SEED-DIM138最新的GB底板,LED的地址可能不太一样,各位需要稍稍留意一下。
下面进入正题:
1. 首先在下载安装.net framework 2.0,然后安装。然后下载安装AISgen,因为AISgen运行依赖于.net framework。
下载地址:
.net framework 2.0 :http://www.crsky.com/soft/4818.html
AISgen : http://www-s.ti.com/sc/techlit/sprab41.zip
2. 打开CCS4,打开LED工程。修改lind.cmd内容如下:
-e _boot
IRAMStart = 0x80000000;
IRAMSize = 0x00020000;
DDRStart = 0xc0000000;
DDRSize = 0x02000000;
DRAMStart = 0x80000000;
_DRAMSize = 0x00020000;
STACKStart = IRAMStart + IRAMSize;
_NAND_EMIFStart = 0x62000000;
_NOR_EMIFStart = 0x60000000;
MEMORY
{
ARM_INTERNAL_RAM (RWXI): origin = 0x80000000 length = 0x00010000
SDRAM (RWXI): org=0xc0000000 length = 0x02000000
}
SECTIONS
{
.boot: load = ARM_INTERNAL_RAM
.text: load > ARM_INTERNAL_RAM
.data: load > ARM_INTERNAL_RAM
.bss: load > ARM_INTERNAL_RAM
}
3. 在evmomapl138.c文件中添加函数
void boot(void)
{
asm(" .global STACKStart");
asm(" .global _stack");
asm(" .global main");
asm(" NOP");
asm(" MRS r0, cpsr");
asm(" BIC r0, r0, #0x1F"); // CLEAR MODES
asm(" ORR r0, r0, #0x13"); // SET SUPERVISOR mode
asm(" ORR r0, r0, #0xC0"); // Disable FIQ and IRQ
asm(" MSR cpsr, r0");
asm(" NOP");
// Set the IVT to low memory, leave MMU & caches disabled
asm(" MRC p15,#0,r0,c1,c0,#0");
asm(" BIC r0,r0,#0x00002300");
asm(" BIC r0,r0,#0x00000087");
asm(" ORR r0,r0,#0x00000002");
asm(" ORR r0,r0,#0x00001000");
asm(" MCR p15,#0,r0,c1,c0,#0");
asm(" NOP");
// Setup the stack pointer
asm(" LDR sp,_stack");
asm(" SUB sp,sp,#4");
asm(" BIC sp, sp, #7");
// Call to main entry point
main();
asm("_stack:");
asm(" .word STACKStart");
}
在evmomapl138.h中添加:
void boot(void);
保存后,编译生成led.out文件。
4. 打开AISgen工具,点击File菜单,选择Load Configuration,选择文件SEED138_AIS_CONFIG.cfg。出现以下界面:
在ARM Application File中选择CCS4生成led.out文件
在AIS Out File中填入led.bin文件的路径。
然后点击 Genrate AIS 生成.bin文件。
5.打开CCS4,连接仿真器,加载GEL文件然后连接板卡
然后运行程序nand-writer_ccs33.out 用来烧写 AIS文件。
![](http://bbs.eeworld.com.cn/images/default/attachimg.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/c8d7767e9be15ceb9f24397dc9758cd4.jpeg)
烧写完成后,断开仿真器,重启开发板,即可看见两个LED灯交替闪烁。
看了上面的说明,有些经验的工程师可能会问,看主函数基本上都是用操作寄存器的方式进行LED控制,那为什么还采用TI的BSL库呢?
原因主要是文中的例程是根据我之前的LED控制加以修改的,整体上尽可能少变动进行固化工作。有些寄存器定义采用了TI的BSL库中的定义,需要进行调用,因此会感觉程序比较大一些。如果各位觉得繁琐的话,完全可以摒弃这些,自己进行精简工作,整体的固化流程是一样的。
到此,各位应该对DIM138的开发有了一个整体上的认识。从芯片认识,新项目建立,开发和调试,最后进行脱机固化有了一个完整的了解,为以后的深入研究打下基础。
在这里我和合众达的所有同事们祝各位工作顺利,项目早日完成。
个人认为,学TI的OMAP也好,学Samsung的arm也好,学MIPS也好。最好的上手的方法就是忘了他是什么。(对于应用开发人员用操作系统下的驱动开发人员适用)
只要有操作系统, 不管他是什么处理器,其本质都只是处理器的核 + 一堆外设
那么对于做应用的人来说,没有任何本质的区别。
对于做驱动的人来说,只是在不同的器件上pinmux设置等东西有所不同。但本质都是为某一个操作系统写驱动。
真正需要去理解硬件内核的架构的人是做编译器的人,大部分工作需要量写汇编代码的人。
对于像OMAPL这样的主流另类,其中之一的DSP或硬件加速器本质上也都是一个外设,只不过可能这个外设本身是可编程的。
这样的外设,通常原厂已经做好一些应用,你只需要根据其编程接口调用即可。如TI提供的Codec。
如果需要运行自己的一些算法,通常原厂也会提供一些通信工程框架。如TI的C6Run及DSPLink
如果需要完全受控,直接写他的memory让启动即可,怎么通信你说了算。