原创 使用 SPBA01B 扩展器的几点心得

2008-8-23 09:22 3083 4 4 分类: MCU/ 嵌入式

第一次玩 SPBA01B ,控制一片28F128J3,结果被它搅得头昏脑胀,差点歇菜,准备彻底放弃它(被客户催的屁滚尿流!!!),改用74LVC374锁存地址和控制信号。但是PCB板已经画好,重新设计来不及了,只好到超市买了一堆雪糕和汽水,彻底降温后静下心来慢慢研究,原来掌握了它的老底还是蛮好用的。其实几个关键点掌握之后就好控制了:



1  CI是做什么的?Cascade in for daisy chain(官方说法:级联XX?找不到合适的词汇形容它)
    其实它类似于复位信号,你的设计不使用级联工作方式的话,它就是复位信号,可以和系统上其它外设的复位信号接在一起(低有效)。你的系统一加电,首先要对系统初始化,外围扩展器件也要一一复位初始化,这时候,给它一个复位就行了。
    如果用一个IO口来控制CI,IO得长期保持高电平。复位的时候设为低电平,再设为高电平并且后面一直保持着。SPBA01B在CI变低一瞬间,开始复位初始化,在CI变高的时候恢复正常。



2  复位之后,设置它的ID号,就是要给它一个名份,在级联方式下,要靠ID号来识别第几个SPBA01B被选中了。就像一夫多妻制下的“大太太”、“二姨太”、“三姨太”等等,得有个辈分头衔的顺序,一个压着一个,免得你争我夺乱了套。它是在SPBA01B的 P_0DH_BMIVolumeID ($000D)寄存器内定义的,通过写入指令来确定。
    单个不级联的SPBA01B是不是就默认自己是老大呢?错!!!你还是得给它写一个ID号,不然它拒绝工作。呵呵!没名份它是不干的(多现实啊!天下有多少个傻女人为心爱的男人默默奉献一切不计名份,结果男人发达之后,嫌她人老珠黄一脚踹!无耻啊!我们一定要做有责任心的好男人,但我不反对多交几个女朋友!!!)。



3  配置工作方式,它必须要明确知道自己应该做啥工作,是IO扩展呢?还是访问存储器?这个要设置 P_23H_BEXConfig ($0023)中的各个相关位,参考一下DataSheet吧,写得挺细的呢!


4  上面三项工作做完之后,它就可以正式开始工作了。注意啊:上面三项没做好的话,它甩都不甩你。如果SPBA01B不睬你,那就要考虑一下上面三个步骤哪里出了差错。



 


5  它是如何工作的呢?(就是工作原理啊,明白了吧!)
    首先,它的肚量只有64K“单元”(字节,为何不直接用字节描述,而是用单元呢?后面慢慢道来。),这是它内部自己用的地址空间(地址线A0-A15,分为高低两个字节分别写入对应的锁存器),和外部扩展用于访问存储器的地址线A0-A21不是一回事,别混淆了。

    然后它又把自己的64K单元空间分成几段,每段的用途各不相同:

    第一段:0x00 - 0x31 是它的寄存器段,所有的控制寄存器全部在这里呢;

    第二段:0x4000 - 0x7FFF,16K单元的范围映射到外部访问的存储器上;

    第三段:0x8000 - 0xBFFF,又是16K单元映射到外部的存储器上;你可能好奇,干嘛不和上面的16K单元合起来统称32K单元呢,呵呵!还是在后面再告诉你吧。

    第四段:0xC000 - 0xFFFF,据称是给RAM留的,它的例子里是这么讲的,但现在是空的,啥用没有!

    所以,你通过访问它不同的地址空间来切换它的功能。当你设定的地址是访问它的0x00-0x31单元,就肯定是对它内部寄存器操作了,它的数据线就和CPU的总线挂在一起了。而它的外部存储器扩展信号,像片选CE#、读信号RE#啊等等全部为高,扩展的存储器不能被访问。

    当你访问0x4000-0xBFFF单元时,就会在它的存储器控制引脚上产生对应的CE#,OE#或WE#等信号,这时候就该外部的存储器工作了。而SPBA01B自己的数据线会彻底悬浮,和CPU数据线脱离。

    按照DataSheet的描述,它最大只能扩展访问 4M Byte,我扩展的28F128J3却有16M Byte,怎么做到的呢?呵呵!现在到解释“单元”这个疑问的时候了。

    虽然SPBA01B内部是8bit结构的,但由于SPEC061A是16bit单片机,而且SPBA01B只管送出扩展的地址线和控制线,和数据线却无关,所以我把存储器的 D0 - D15 数据线挂到单片机的IOA0 - IOA15上,这样访问的数据量大了一倍,可以访问到8M Byte,然后SPBA01B在4M Byte扩展模式下还多了一个IO口可以用,我用它作为最高的一根地址线,最终访问量达到了16M Byte,所以,我前面的描述使用了“单元”,而不是“字节”。


6  DataSheet上是这样说的,外部存储器被分为32KB为一段,映射到SPBA01B的0x4000 - 0xBFFF地址空间内......我郑重警告你!这是骗你的!严重骗你的!非常不负责任的骗你的!!!

    其实,外部存储器是分作16K一段的,这就是我上面为何不把 0x4000 - 0xBFFF 说成32K单元,而是一定要分成两块的重要原因!可能是IC的设计工程师偷懒(对不起哦!我说话挺直!),所以把编写软件搞复杂了。

    你通过SPBA01B访问外部扩展存储器的时候,那个0x0000 - 0x8000指定的32K线性单元空间不能通过简单的加上偏移量0x4000就能访问了,而是要把它平分为两块,它们的块顺序上下颠倒过来访问(每个块内的地址单元是不需要颠倒的,还是正常顺序!!!)!!!???

  好奇怪哦?要分成 0x0000-0x3FFF 和 0x4000-0x7FFF 这两段,第一段单元空间必须要加上 0x8000 的偏移量才能正确访问,变成了 0x8000 - 0xBFFF ,第二段不用作任何改动直接访问即可。奇怪吧???其实一点也不奇怪,因为这样设计在逻辑电路上实现起来最简单,通过几个最原始的逻辑门电路就能实现,如果整个32K线性单元全部平移,就要动用加法器或类似的复杂逻辑电路了,不仅设计电路麻烦,成本也增加了。

    硬件工程师经常喜欢这样搞,还沾沾自喜:“嘿嘿!我又省了几毛钱了......”,软件工程师跟在后面倒霉了,被复杂的逻辑关系绕的头昏眼花,还喃喃自语:“我咋这么笨啊.....”。呵呵!老板们可能要吹胡子瞪眼了:“成本最重要!当然应该这么做了!”。可是,从性价比上看不是样样都划算的!硬件工程师只需要多花几小时就能做好的功能,却因为偷懒省掉的零件,可能需要软件工程师几周的工作量来弥补,甚至留下难以察觉的BUG在产品售出后才发现,拖延上市和产品召回的损失远远大于省下成本的n倍!!!


      所以,这里郑重宣布,SPBA01B的外部扩展存储器,是16K一段的,绝对不能当作32K来用,否则:访问扩展的SRAM绝对没问题,FLASH和ROM就惨啦!!!!!!


7  用于存储器扩展的SPBA01B配置内部寄存器的要点。就是关键一点,段寄存器的bit7一定要置为“1”!!!就是你算出来段编号写入段寄存器之前,一定要记得“或”运算0x80!!!看看Datasheet上的“must be1”这行字,我就是开始没注意它,存储器的CE#(片选信号)死都不肯出来,就算OE#和WE#跑的再欢也是没用的。



 


8  最后,最关键的部分:如何对SPBA01B进行读写操作?很重要,很让人糊涂,很容易让人暴跳如雷!呵呵!真的不骗你。

    IC工程师对这个双线操作设计很得意、很自豪,因为很节省资源!?......其实这种操作真的很讨厌!SPBA01B用MC1和MC0输入信号来产生四个访问状态:锁定高地址(MC1=1、MC0=1)、锁定低地址(MC1=1、MC0=0)、读操作(MC1=0、MC0=1)、写操作(MC1=0、MC0=0)。

    工作原理就是三个步骤:1.先把准备访问SPBA01B内部单元的高8位地址送到它的高地址锁存器;2.然后再轮到低8位锁存器;3.最后你再进行需要的读或写操作。

    麻烦就在于它没有片选信号,这两根状态线肯定导致了SPBA01B每时每刻肯定是在某一种工作状态下,你对数据线的每个操作都要小心,免得干扰SPBA01B或者被它干扰!!!

    你可能会这样设计你的软件,下面我用伪码描述工作原理,请注意!别真的在IDE里面编译它:



 


    1.  IOA_Data = Adder_H_8;    //  送上高八位地址数据
    2.  MC1 = 1;                            //  将状态设置为锁存高地址
    3.  MC0 = 1;                           
    4.  IOA_Data = Adder_L_8;    //  送上低八位地址数据
    5.  MC1 = 1;                            //  将状态设置为锁存低地址
    6.  MC0 = 0;
    7.  IOA_Data = Data;              //  送上准备写入的数据
    8.  MC1 = 1;                            //  将状态设置写数据
    9.  MC0 = 1                           



 


    好咯!写完咯!成功咯!!!真的吗???慢着!!!!!!!!!!!!!!!!!!!!!


如果你的软件真的这样写就完蛋了,就像准备到东方,却一直向西方走,而且不是在地球上,还能绕回来。就像困在一个无边的二维世界里,永远到不了你想去的地方。

    为何呢?因为数据并没有正确锁存到地址寄存器,而且写入扩展存储器或者SPBA01B寄存器的数据是随机数!

    这种情况是怎么产生的呢?就是因为SPBA01B采用了上升沿锁存机制。纯做软件的朋友可能不太明白这个工作原理,学过逻辑电路的朋友就明白了。不明白不要紧,看看我们应该怎样正确的做就行了。

    上面的伪代码有两个毛病,正确的软件编写方式应该如下:

1.  MC1=1;                              //  首先设定为写高地址模式
2.  MC0=1;
3.  IOA_Data = Adder_H_8;    //  送上高八位地址数据
4.  MC1=1;                              //  再设为写低地址模式,注意:由于在这里信号线的跳变,高八位地址同时被正确锁存到高地址寄存器中了
5.  MC0=0;
6.  IOA_Data = Adder_L_8;    //  再送上低八位地址
7.  MC1=0;                              //  设定为写数据模式,注意:由于在这里信号线的跳变,低八位地址同时被正确锁存到高地址寄存器中了
8.  MC0=0;
9.  IOA_Data = Data;              //  送上准备写入的数据。!!!!!注意:这里很关键的一点,很多人会忽略掉!!!!!数据是送上数据线了,但是还没有开始真正的写操作!!!!!需要将信号线跳变一下,确保写操作开始执行,数据正确写入该去的地方!!!!!
10. MC1=1;                              //  这里就是跳变信号,确保上面的写操作正确执行。
11. MC0=1;

    你可能会担心,信号又跳回锁存高地址模式,会不会造成不良影响啊???那么~~~  接吻会不会怀孕啊???其实这就是告诉你的答案!当然不会有影响啦!因为只要MC1和MC0保持那个状态不变,指定的模式也就没有真正开始执行!所以不会改变高地址。两个人拖拖手、亲亲嘴,你别开始玩真刀真枪的,哪里会有BB啊???


    烦啊!你明白为何我讨厌这种双线操作了吧。有没有办法关闭这个讨厌的四个指定状态呢?免得不小心对SPBA01B误操作了。有,肯定有,而且方法很多,但不花钱的办法可能只有一个,和老太太的裹脚布一样,又臭又长。记得前面说过的ID号吗?就它管用,往这个寄存器里面写一个和它不一致的ID号,它就立刻休息了。需要它的时候,再写回它自己的ID号就行了,它立马又活蹦乱跳了。这个功能是设计给级联的需求使用的,这里可以借用,明白了吧!如果不懂级联是什么?那么好好看看DataSheet就知道了。


    假如你嫌烦,不愿看DataSheet,那我就简单介绍一下:SPBA01B是可以串接起来工作的,最多可以串七个,它们共享MC1和MC0控制线。为了保证它们正常工作不打架,每个SPBA01有自己的专用ID号,不能和其它重复(否则那就乱来了)。第一个SPBA01B的CI信号接到CPU的IO上,然后有个CO信号接到下一片SPBA01B的CI上,该片的Co再接到第三级的CI上,如此这般...一直连到第七个。

    当CPU开始对SPBA01B复位时,第一片复位了,等待CPU写一个ID号给它,写完成后,这个ID号在后面的工作中就不能改了,直到你关闭电源(再用CI信号来次复位后重写呢?我忘记做试验了,有兴趣的朋友试试看吧)。然后,如果你再次写入ID号的时候,它开始比较,如果ID号相同,表示你确定要求它是活动的,否则,你是选择另外的SPBA01B,这时候,它开始传递CI信号到第二个SPBA01B(通过它的CO到第二片的CI),然后它睡了。第二个开始确认,如果没写过ID,就把ID号当作自己的名份保存起来(快抢啊!谁愿意做名份最小的?)。如果自己是写过ID的,那么它就比较ID号,重复第一片同样的工作......这样就把所有的ID号权设置完了,最后设置完的保持活动状态,其它全部睡着了。幸好SPBA01B不会吃醋,不然下面几级全部饿死了......!!!如果你需要唤醒某个开始工作,则写ID号到ID号寄存器,对上号的马上醒来,刚才那个又睡去了!!!


    终于写完了,天亮了,我也要睡了!各位拜拜!希望对各位有帮助!!!

文章评论0条评论)

登录后参与讨论
我要评论
0
4
关闭 站长推荐上一条 /2 下一条