完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
MEMORY
{ PAGE 0 : OTP : origin = 0x3D7800, length = 0x000800 FLASHJ : origin = 0x3D8000, length = 0x002000 FLASHI : origin = 0x3DA000, length = 0x002000 FLASHH : origin = 0x3DC000, length = 0x004000 FLASHG : origin = 0x3E0000, length = 0x004000 FLASHF : origin = 0x3E4000, length = 0x004000 FLASHE : origin = 0x3E8000, length = 0x004000 FLASHD : origin = 0x3EC000, length = 0x004000 FLASHC : origin = 0x3F0000, length = 0x004000 FLASHB : origin = 0x3F4000, length = 0x002000 FLASHA : origin = 0x3F6000, length = 0x001FF6 BEGIN : origin = 0x3F7FF6, length = 0x000002 PASSWDS : origin = 0x3F7FF8, length = 0x000008 ROM : origin = 0x3FF000, length = 0x000FC0 VECTORS : origin = 0x3FFFC2, length = 0x00003E PAGE 1 : /* SARAM */ RAMM0M1 : origin = 0x000000, length = 0x000800 /* Peripheral Frame 0: */ DEV_EMU : origin = 0x000880, length = 0x000180 FLASH_REGS : origin = 0x000A80, length = 0x000060 CSM : origin = 0x000AE0, length = 0x000010 XINTF : origin = 0x000B20, length = 0x000020 CPU_TIMER0 : origin = 0x000C00, length = 0x000008 CPU_TIMER1 : origin = 0x000C08, length = 0x000008 CPU_TIMER2 : origin = 0x000C10, length = 0x000008 PIE_CTRL : origin = 0x000CE0, length = 0x000020 PIE_VECT : origin = 0x000D00, length = 0x000100 /* Peripheral Frame 1: */ ECAN_A : origin = 0x006000, length = 0x000100 ECAN_AMBOX : origin = 0x006100, length = 0x000100 /* Peripheral Frame 2: */ SYSTEM : origin = 0x007010, length = 0x000020 SPI_A : origin = 0x007040, length = 0x000010 SCI_A : origin = 0x007050, length = 0x000010 XINTRUPT : origin = 0x007070, length = 0x000010 GPIOMUX : origin = 0x0070C0, length = 0x000020 GPIODAT : origin = 0x0070E0, length = 0x000020 ADC : origin = 0x007100, length = 0x000020 EV_A : origin = 0x007400, length = 0x000040 EV_B : origin = 0x007500, length = 0x000040 SPI_B : origin = 0x007740, length = 0x000010 SCI_B : origin = 0x007750, length = 0x000010 MCBSP_A : origin = 0x007800, length = 0x000040 /* CSM Password Locations */ CSM_PWL : origin = 0x3F7FF8, length = 0x000008 /* SARAM */ RAML0L1 : origin = 0x008000, length = 0x002000 RAMH0 : origin = 0x3F8000, length = 0x002000 } SECTIONS { /* Allocate program areas: */ .reset : 》 BEGIN PAGE = 0 vectors : 》 VECTORS PAGE = 0 .cinit : 》 FLASHJ PAGE = 0 .text : 》 FLASHA PAGE = 0 /* Allocate data areas: */ .stack : 》 RAMM0M1 PAGE = 1 .bss : 》 RAML0L1 PAGE = 1 .ebss : 》 RAML0L1 PAGE = 1 .const: load = FLASHB PAGE 0, run = RAML0L1 PAGE 1 { /* Get Run Address */ __const_run = 。; /* Mark Load Address*/ *(.c_mark) /* Allocate .const */ *(.const) /* Compute Length */ __const_length = 。-__const_run; } .econst: load = FLASHB PAGE 0, run = RAML0L1 PAGE 1 { /* Get Run Address */ __econst_run = 。; /* Mark Load Address*/ *(.ec_mark) /* Allocate .const */ *(.econst) /* Compute Length */ __econst_length = 。-__econst_run; } .sysmem : 》 RAMH0 PAGE = 1 /* Allocate IQ math areas: */ IQmath : 》 FLASHI PAGE = 0 /* Math Code */ IQmathFastTables : 》 FLASHI PAGE = 0 /* Math Tables in fast memory */ IQmathTables : 》 ROM PAGE = 0 /* Math Tables In ROM */ /* Allocate Peripheral Frame 0 Register Structures: */ DevEmuRegsFile : 》 DEV_EMU PAGE = 1 FlashRegsFile : 》 FLASH_REGS PAGE = 1 CsmRegsFile : 》 CSM PAGE = 1 XintfRegsFile : 》 XINTF PAGE = 1 CpuTimer0RegsFile : 》 CPU_TIMER0 PAGE = 1 CpuTimer1RegsFile : 》 CPU_TIMER1 PAGE = 1 CpuTimer2RegsFile : 》 CPU_TIMER2 PAGE = 1 PieCtrlRegsFile : 》 PIE_CTRL PAGE = 1 PieVectTable : 》 PIE_VECT PAGE = 1 /* Allocate Peripheral Frame 2 Register Structures: */ ECanaRegsFile : 》 ECAN_A PAGE = 1 ECanaMboxesFile : 》 ECAN_AMBOX PAGE = 1 /* Allocate Peripheral Frame 1 Register Structures: */ SysCtrlRegsFile : 》 SYSTEM PAGE = 1 SpiaRegsFile : 》 SPI_A PAGE = 1 SciaRegsFile : 》 SCI_A PAGE = 1 XIntruptRegsFile : 》 XINTRUPT PAGE = 1 GpioMuxRegsFile : 》 GPIOMUX PAGE = 1 GpioDataRegsFile : 》 GPIODAT PAGE = 1 AdcRegsFile : 》 ADC PAGE = 1 EvaRegsFile : 》 EV_A PAGE = 1 EvbRegsFile : 》 EV_B PAGE = 1 ScibRegsFile : 》 SCI_B PAGE = 1 McbspaRegsFile : 》 MCBSP_A PAGE = 1 /* CSM Password Locations */ CsmPwlFile : 》 CSM_PWL PAGE = 1 } 然后把lib文件替换成:rts2800_fl040830.lib 就可以啦。 希望你也成功!!! 本人qq: 15007807 欢迎以后交流。 点评: FLASH不用分这么细的,编大一点的程序会有麻烦 以下是本人做过的某项目CMD文件 MEMORY { PAGE 0 : /* For this example, H0 is split between PAGE 0 and PAGE 1 */ /* BEGIN is used for the “boot to HO” bootloader mode */ /* RESET is loaded with the reset vector only if */ /* the boot is from XINTF Zone 7. Otherwise reset vector */ /* is fetched from boot ROM. See .reset section below */ RAMM0 : origin = 0x000000, length = 0x000400 BEGIN : origin = 0x3f7ff6, length = 0x000002 PRAMH0 : origin = 0x3d8000, length = 0x004000 /* */ RESET : origin = 0x3FFFC0, length = 0x000002 PAGE 1 : /* For this example, H0 is split between PAGE 0 and PAGE 1 */ LSARAM : origin = 0x3de000, length = 0x008000 /* */ RAMM1 : origin = 0x000400, length = 0x000400 DRAMH0 : origin = 0x3f8000, length = 0x001000 /* */ BUFA : origin = 0x8000, length=0x1000 BUFB : origin = 0x9000, length=0x1000 BUFC : origin = 0x3f9000, length=0x1000 RECORDDATA : origin = 0x80020, length=0x700 PARAMETER : origin = 0x80725, length=0x20 PASSWORD : origin = 0x80005, length=0x08 } SECTIONS { /* Setup for “boot to H0” mode: The codestart section (found in DSP28_CodeStartBranch.asm) re-directs execution to the start of user code. Place this section at the start of H0 */ codestart : 》 BEGIN, PAGE = 0 ramfuncs : 》 PRAMH0 PAGE = 0 .text : 》 PRAMH0, PAGE = 0 .cinit : 》 PRAMH0, PAGE = 0 .pinit : 》 PRAMH0, PAGE = 0 .switch : 》 RAMM0, PAGE = 0 .reset : 》 RESET, PAGE = 0, TYPE = DSECT /* not used, */ .stack : 》 RAMM1, PAGE = 1 .ebss : 》 DRAMH0, PAGE = 1 .econst : 》 LSARAM, PAGE = 1 .esysmem : 》 DRAMH0, PAGE = 1 .bufferA : {}》BUFA PAGE 1 .bufferB : {}》BUFB PAGE 1 .bufferC : {}》BUFC PAGE 1 .RecordData : {}》RECORDDATA PAGE 1 .parameter : {}》PARAMETER PAGE 1 .password : {}》PASSWORD PAGE 1 } 2007.8.13 19:04 作者:wind 收藏 | 评论:0 于DDS的简单介绍 分类:默认栏目DDS同DSP(数字信号处理)一样,是一项关键的数字化威廉希尔官方网站 。DDS是直接数字式频率合成器(Direct Digital Synthesizer)的英文缩写。与传统的频率合成器相比,DDS具有低成本、低功耗、高分辨率和快速转换时间等优点,广泛使用在电信与电子仪器领域,是实现设备全数字化的一个关键威廉希尔官方网站 。 一块DDS芯片中主要包括频率控制寄存器、高速相位累加器和正弦计算器三个部分(如Q2220)。频率控制寄存器可以串行或并行的方式装载并寄存用户输入的频率控制码;而相位累加器根据频率控制码在每个时钟周期内进行相位累加,得到一个相位值;正弦计算器则对该相位值计算数字化正弦波幅度(芯片一般通过查表得到)。DDS芯片输出的一般是数字化的正弦波,因此还需经过高速D/A转换器和低通滤波器才能得到一个可用的模拟频率信号。 另外,有些DDS芯片还具有调幅、调频和调相等调制功能及片内D/A变换器(如AD7008)。 DDS有如下优点:⑴ 频率分辨率高,输出频点多;⑵频率切换速度快,可达us量级;⑶ 频率切换时相位连续;⑷ 可以输出宽带正交信号;⑸ 输出相位噪声低,对参考频率源的相位噪声有改善作用;⑹可以产生任意波形;⑺ 全数字化实现,便于集成,体积小,重量轻,因此八十年代以来各国都在研制和发展各自的DDS产品,如美国QUALCOMM公司的Q2334,Q2220;STANFORD公司的STEL-1175,STEL-1180;AD公司的AD7008,AD9850,AD9854等。这些DDS芯片的时钟频率从几十兆赫兹到几百兆赫兹不等,芯片从一般功能到集成有D/A转换器和正交调制器。 2007.8.13 18:44 作者:wind 收藏 | 评论:0 使用C/C 语言编写基于DSP程序的注意事项 分类:默认栏目 1、 不影响执行速度的情况下,可以使用c或c/c++语言提供的函数库,也可以自己设计函数,这样更易于使用“裁缝师”优化处理,例如:进行绝对值运算,可以调用fabs()或abs()函数,也可以使用if.。.else.。.判断语句来替代。 2、 要非常谨慎地使用局部变量,根据自己项目开发的需要,应尽可能多地使用全局变量和静态变量。 3、 一定要非常重视中断向量表的问题,很多朋友对中断向量表的调用方式不清楚。其实中断向量表中的中断名是任意取定的,dsp是不认名字的,它只认地址!!中断向量表要重新定位。这一点很重要。 4、 要明确dsp软件开发的第一步是对可用存储空间的分析,存储空间分配好坏关系到一个dsp程序员的水平。对于dsp,我们有两种名称的存储空间,一种是物理空间,另一种是映射空间。物理空间是dsp上可以存放数据和程序的实际空间(包括外部存储器),我们的数据和程序最终放到物理空间上,但我们并不能直接访问它们。我们要访问物理空间,必须借助于映射空间才行!!但是映射空间本身是个“虚”空间,是个不存在的空间。所以,往往是映射空间远远大于实际的物理空间,有些映射空间,如io映射空间,它本身还代表了一种接口。只有那些物理空间映射到的映射空间才是我们真正可访问(读或写)的存储空间。 5、 尽可能地减少除法运算,而尽可能多地使用乘法和加法运算代替。 6、 如果ti公司或第三方软件合作商提供了dsplib或其他的合法子程序库供调用,应尽可能地调用使用。这些子程序均使用用汇编写成,更为重要之处是通过了tms320算法标准测试。而且,常用的数字信号处理算法均有包括!! 7、 尽可能地采用内联函数!!而不用一般的函数!!可以提高代码的集成度。 8、 编程风格力求简炼!!尽可能用c语言而不用c++语言。我个人感到虽然c++终代码长了一些,好象对执行速度没有影响。 9、 因为在c5000中double型和float型均占有2个字,所以都可以使用,而且,可以直接将int型赋给float型或double型,但,尽可能地多使用int数据类型代替!这一点需要注意!! 10、 程序最后至少要加上一个空行,编译器当这个空行为结尾提示符。 11、 大胆使用位运算符,非常好用!! 2007.8.13 18:09 作者:wind 收藏 | 评论:0 CMD文件的作用 分类:默认栏目在DSP里,程序执行过程中也有好多地方需要跳转,所以需要跳转的目标地址。如果你在编程序时知道你所要跳转的地址,那就更好的,但实际上,这是很不好控制的。所以就产生了.CMD。它有一个最大的好处,可以把每个段分配地址,所以比如你想从一个段跳到另一个段的时候,就很方便的知道这个段的起始地址。 CMD 它是用来分配rom和ram空间用的,告诉链接程序怎样计算地址和分配空间。所以不同的芯片就有不同大小的rom和ram.放用户程序的地方也不尽相同。所以要根据你的芯片进行修改.cmd文件分两部分.MEMORY和SECTIONS. MEMORY { PAGE 0 。..。..。..。 PAGE 1.。..。..。. } SECTIONS {SECTIONS { .vectors 。..。..。..。..。..。. .reset 。..。..。..。..。..。. 。..。..。..。..。..。 } MEMORY是用来指定芯片的rom和ram的大小和划分出几个区间。 PAGE 0 对应rom;PAGE 1对应ram PAGE 里包含的区间名字与其后面的参数反映了该区间的起始地址和长度。 如: PAGE 0 : VECS(区间名字): origin(起始地址) = 0h , length (长度)=040h /*VECTORS*/ SECTIONS:(在程序里添加下面的段名如.vectors.用来指定该段名以下,另一个段名以上的程序(属于PAGE0)或数据(属于PAGE1)放到“》”符号后的空间名字所在的地方。 如引用字段名“.vectors ”的程序或数据将被放到VECS ,VECS是PAGE0即是ROM空间 00H至40H的地方 SECTIONS { .vectors : { } 》 VECS PAGE 0 /* Interrupt vector table */ .reset : { } 》 VECS PAGE 0 /* Reset code */ 。..。..。..。.. } .vectors,.reset都是段名。 加不加“。”随你便,即.vectors表示名为 “.vectors”的段。 {}表示段的全部,{}》 VECS PAGE 0表示将段的全部放入名为 VECS PAGE 0的内存区。 example: /****************************************************************************/ /******** Specify the memory configuration **********************************/ /****************************************************************************/ MEMORY { PAGE 0: VECS: origin = 00000h, length = 00040h LOW: origin = 00040h, length = 03FC0h SARAM: origin = 04000h, length = 00800h B0: origin = 0FF00h, length = 00100h PAGE 1: B0: origin = 00200h, length = 00100h B1: origin = 00300h, length = 00100h B2: origin = 00060h, length = 00020h SARAM: origin = 08000h, length = 00800h } /*--------------------------------------------------------------------------*/ /* SECTIONS ALLOCATION */ /*--------------------------------------------------------------------------*/ SECTIONS { .text : { } 》 LOW PAGE 0 .cinit : { } 》 LOW PAGE 0 .switch : { } 》 LOW PAGE 0 .const : { } 》 SARAM PAGE 1 .data : { } 》 SARAM PAGE 1 .bss : { } 》 SARAM PAGE 1 .stack : { } 》 SARAM PAGE 1 .sysmem : { } 》 SARAM PAGE 1 } 1,系统定义: .cinit 存放C程序中的变量初值和常量; .const 存放C程序中的字符常量、浮点常量和用const声明的常量; .switch 存放C程序中switch语句的跳针表; .text 存放C程序的代码; .bss 为C程序中的全局和静态变量保留存储空间; .far 为C程序中用far声明的全局和静态变量保留空间; .stack 为C程序系统堆栈保留存储空间,用于保存返回地址、函数间的参数传递、存储局部变量和保存中间结果; .sysmem 用于C程序中malloc、calloc和realloc函数动态分配存储空间 2,用户定义: #pragma CODE_SECTION (symbol, “section name”; #pragma DATA_SECTION (symbol, “section name” DSP的C语言的特殊性 大家在使用51系列C语言时已经注意到,控制器的C语言和PC机上使用的C有一个显著的特点:经常要对硬件操作,程序中有大量针对控制器内部资源进行操作的语句。所以,开发者要明白怎样用C语言来操纵控制器的内部资源,既怎样用C语句操作寄存器和内部存储器等。 举个例子,在51汇编中我们写 MOV A,#20H,汇编程序能够识别A是指累加器,而在51 C程序中我们写 ACC=32;,编译器能够识别ACC是指累加器而不是一般的变量。即每一个寄存器都有一个专有名字供开发者使用,它们定义在一个头文件reg51.h 中,程序员只需在程序的开始部分用#include“reg51.h”语句将该文件包含进来即可。注意:这些寄存器的名字不能用做变量名。 同样,在TMS320F240的C语言中也有一个头文件C240.H定义各个寄存器的名称,这里摘录几条语句进行介绍。 比如:#define IMR ((PORT)0x0004) #define XINT1_CR ((PORT)0x07070) IMR 、XINT1_CR就对应两个寄存器,实际是寄存器的地址,用高级语言的说法是指针。我们也在程序的开始部分用#include“c240.h”语句将该文件包含进来。这样,在DSP的C语言中使用它们只需在前面加一个星号(*),例如, *IMR=0X1010;/* 将16进制数1010H赋给IMR寄存器 */ *XINT1_CR=0X0A0B0;/*将16进制数A0B0H赋给XINT1_CR寄存器 */ 最好将c240.h这个文件打印出来,弄清楚各个寄存器的定义名称。 TMS320F240芯片的C语言开发过程 简单地说,整个过程包括以下五个步骤: 1、编辑C语言源程序 2、编译源程序(注意编译参数) 3、链接目标文件(注意用CMD文件) 4、在线仿真 5、固化程序 2007.8.13 17:06 作者:wind 收藏 | 评论:0 电容的作用 分类:默认栏目1.电容器主要用于交流电路及脉冲电路中,在直流电路中电容器一般起隔断直流的作用。 2.电容既不产生也不消耗能量,是储能元件。 3.电容器在电力系统中是提高功率因数的重要器件;在电子电路中是获得振荡、滤波、相移、旁路、耦合等作用的主要元件。 4.因为在工业上使用的负载主要是电动机感性负载,所以要并电容这容性负载才能使电网平衡。 5.在接地线上,为什么有的也要通过电容后再接地咧? 答:在直流电路中是抗干扰,把干扰脉冲通过电容接地(在这次要作用是隔直——电路中的电位关系);交流电路中也有这样通过电容接地的,一般容量较小,也是抗干扰和电位隔离作用。 6.电容补尝功率因数是怎么回事? 答:因为在电容上建立电压首先需要有个充电过程,随着充电过程,电容上的电压逐步提高,这样就会先有电流,后建立电压的过程,通常我们叫电流超前电压90度(电容电流回路中无电阻和电感元件时,叫纯电容电路)。电动机、变压器等有线圈的电感电路,因通过电感的电流不能突变的原因,它与电容正好相反,需要先在线圈两端建立电压,后才有电流(电感电流回路中无电阻和电容时,叫纯电感电路),纯电感电路的电流滞后电压90度。由于功率是电压乘以电流,当电压与电流不同时产生时(如:当电容器上的电压最大时,电已充满,电流为0;电感上先有电压时,电感电流也为0),这样,得到的乘积(功率)也为0!这就是无功。那么,电容的电压与电流之间的关系正好与电感的电压与电流的关系相反,就用电容来补偿电感产生的无功,这就是无功补偿的原理。 2007.8.13 15:16 作者:wind 收藏 | 评论:0 volatile的用法 分类:默认栏目避免编译器优化的用法 转自《海涛的笔记》 _lindwen volatile的本意是“易变的” 由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如: static int i=0; int main(void) { 。.. while (1) { if (i) dosomething(); } } /* Interrupt service routine. */ void ISR_2(void) { i=1; } 程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此 可能只执行一次对从i到某寄存器的读***作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被 调用。如果将将变量加上volatile修饰,则编译器保证对此变量的读写***作都不会被优化(肯定执行)。此例中i也应该如此说明。 一般说来,volatile用在如下的几个地方: 1、中断服务程序中修改的供其它程序检测的变量需要加volatile; 2、多任务环境下各任务间共享的标志应该加volatile; 3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义; 另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实 现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。 volatile 的含义 volatile总是与优化有关,编译器有一种威廉希尔官方网站 叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以死代码消除。但有时这些优化不是程序所需要的,这时可以用volatile关键字禁止做这些优化,volatile的字面含义是易变的,它有下面的作用: 1 不会在两个***作之间把volatile变量缓存在寄存器中。在多任务、中断、甚至setjmp环境下,变量可能被其他的程序改变,编译器 自己无法知道,volatile就是告诉编译器这种情况。 2 不做常量合并、常量传播等优化,所以像下面的代码: volatile int i = 1; if (i 》 0) 。.. if的条件不会当作无条件真。 3 对volatile变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到,编译器常常可以省略那个赋值***作,然而对Memory Mapped IO的处理是不能这样优化的。 前面有人说volatile可以保证对内存***作的原子性,这种说法不大准确,其一,x86需要LOCK前缀才能在SMP下保证原子性,其二,RISC根本不能对内存直接运算,要保证原子性得用别的方法,如atomic_inc。 对于jiffies,它已经声明为volatile变量,我认为直接用jiffies++就可以了,没必要用那种复杂的形式,因为那样也不能保证原子性。 你可能不知道在Pentium及后续CPU中,下面两组指令 inc jiffies ;; mov jiffies, %eax inc %eax mov %eax, jiffies 作用相同,但一条指令反而不如三条指令快。 现举例说明(以Keil-c与a51为例 例子来自Keil FQA),看完例子后你应该明白volatile的意思了,如果还不明白,那只好 再看一遍了。 例1. void main (void) { volatile int i; int j; i = 1; //1 不被优化 i=1 i = 2; //2 不被优化 i=1 i = 3; //3 不被优化 i=1 j = 1; //4 被优化 j = 2; //5 被优化 j = 3; //6 j = 3 } --------------------------------------------------------------------- 例2. 函数: void func (void) { unsigned char xdata xdata_junk; unsigned char xdata *p = &xdata_junk; unsigned char t1, t2; t1 = *p; t2 = *p; } 编译的汇编为: 0000 7E00 R MOV R6,#HIGH xdata_junk 0002 7F00 R MOV R7,#LOW xdata_junk ;---- Variable ‘p’ assigned to Register ‘R6/R7’ ---- 0004 8F82 MOV DPL,R7 0006 8E83 MOV DPH,R6 ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 注意 0008 E0 MOVX A,@DPTR 0009 F500 R MOV t1,A 000B F500 R MOV t2,A ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 000D 22 RET 将函数变为: void func (void) { volatile unsigned char xdata xdata_junk; volatile unsigned char xdata *p = &xdata_junk; unsigned char t1, t2; t1 = *p; t2 = *p; } 编译的汇编为: 0000 7E00 R MOV R6,#HIGH xdata_junk 0002 7F00 R MOV R7,#LOW xdata_junk ;---- Variable ‘p’ assigned to Register ‘R6/R7’ ---- 0004 8F82 MOV DPL,R7 0006 8E83 MOV DPH,R6 ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 0008 E0 MOVX A,@DPTR 0009 F500 R MOV t1,A ;a处 000B E0 MOVX A,@DPTR 000C F500 R MOV t2,A ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 000E 22 RET 比较结果可以看出来,未用volatile关键字时,只从*p所指的地址读一次 如在a处*p的内容有变化,则t2得到的则不是真正*p的内容。 --------------------------------------------------------------------- 例3 volatile unsigned char bdata var; // use volatile keyword here ***it var_0 = var^0; ***it var_1 = var^1; unsigned char xdata values[10]; void main (void) { unsigned char i; for (i = 0; i 《 sizeof (values); i++) { var = values; if (var_0) { var_1 = 1; //a处 values = var; // without the volatile keyword, the compiler // assumes that ‘var’ is unmodified and does not // reload the variable content. } } } 在此例中,如在a处到下一句运行前,var如有变化则不会,如var=0xff; 则在 values = var;得到的还是values = 1; --------------------------------------------------------------------- 应用举例: 例1. #define DBYTE ((unsigned char volatile data *) 0) 说明:此处不用volatile关键字,可能得不到真正的内容。 --------------------------------------------------------------------- 例2. #define TEST_VOLATILE_C //*************************************************************** // verwendete Include Dateien //*************************************************************** #if __C51__ 《 600 #error: !! Keil 版本不正确 #endif //*************************************************************** // 函数 void v_IntOccured(void) //*************************************************************** extern void v_IntOccured(void); //*************************************************************** // 变量定义 //*************************************************************** char xdata cValue1; //全局xdata char volatile xdata cValue2; //全局xdata //*************************************************************** // 函数: v_ExtInt0() // 版本: // 参数: // 用途:cValue1++,cValue2++ //*************************************************************** void v_ExtInt0(void) interrupt 0 { cValue1++; cValue2++; } //*************************************************************** // 函数: main() // 版本: // 参数: // 用途:测试volatile //*************************************************************** void main() { char cErg; //1. 使cErg=cValue1; cErg = cValue1; //2. 在此处仿真时手动产生中断INT0,使cValue1++; cValue2++ if (cValue1 != cErg) v_IntOccured(); //3. 使cErg=cValue2; cErg = cValue2; //4. 在此处仿真时手动产生中断INT0,使cValue1++; cValue2++ if (cValue2 != cErg) v_IntOccured(); //5. 完成 while (1); } //*************************************************************** // 函数: v_IntOccured() // 版本: // 参数: // 用途: 死循环 //*************************************************************** void v_IntOccured() { while(1); } 仿真可以看出,在没有用volatile时,即2处,程序不能进入v_IntOccured(); 但在4处可以进入v_IntOccured(); 2007.8.13 09:41 作者:wind 收藏 | 评论:0 结构体(1) 分类:默认栏目什么是结构体? 简单的来说,结构体就是一个可以包含不同数据类型的一个结构,它是一种可以自己定义的数据类型,它的特点和数组主要有两点不同,首先结构体可以在一个结构中声明不同的数据类型,第二相同结构的结构体变量是可以相互赋值的,而数组是做不到的,因为数组是单一数据类型的数据集合,它本身不是数据类型(而结构体是),数组名称是常量指针,所以不可以做为左值进行运算,所以数组之间就不能通过数组名称相互复制了,即使数据类型和数组大小完全相同。 定义结构体使用struct修饰符,例如: struct test { float a; int b; }; 上面的代码就定义了一个名为test的结构体,它的数据类型就是test,它包含两个成员a和b,成员a的数据类型为浮点型,成员b的数据类型为整型。 由于结构体本身就是自定义的数据类型,定义结构体变量的方法和定义普通变量的方法一样。 test pn1; 这样就定义了一test结构体数据类型的结构体变量pn1,结构体成员的访问通过点操作符进行,pn1.a=10 就对结构体变量pn1的成员a进行了赋值操作。 注意:结构体生命的时候本身不占用任何内存空间,只有当你用你定义的结构体类型定义结构体变量的时候计算机才会分配内存。 结构体,同样是可以定义指针的,那么结构体指针就叫做结构指针。 结构指针通过-》符号来访问成员,下面我们就以上所说的看一个完整的例子: #include 《iostream》 #include 《string》 using namespace std; struct test//定义一个名为test的结构体 { int a;//定义结构体成员a int b;//定义结构体成员b }; void main() { test pn1;//定义结构体变量pn1 test pn2;//定义结构体变量pn2 pn2.a=10;//通过成员操作符。给结构体变量pn2中的成员a赋值 pn2.b=3;//通过成员操作符。给结构体变量pn2中的成员b赋值 pn1=pn2;//把pn2中所有的成员值复制给具有相同结构的结构体变量pn1 cout《《pn1.a《《“|”《《pn1.b《《endl; cout《《pn2.a《《“|”《《pn2.b《《endl; test *point;//定义结构指针 point=&pn2;//指针指向结构体变量pn2的内存地址 cout《《pn2.a《《“|”《《pn2.b《《endl; point-》a=99;//通过结构指针修改结构体变量pn2成员a的值 cout《《pn2.a《《“|”《《pn2.b《《endl; cout《《point-》a《《“|”《《point-》b《《endl; cin.get(); } 总之,结构体可以描述数组不能够清晰描述的结构,它具有数组所不具备的一些功能特性。 2007.8.12 20:31 作者:wind 收藏 | 评论:0 位段 分类:默认栏目位段,将一个字节分为几段来存放几个信息。所谓位段是以位为单位定义长度的结构体类型中的成员。如: struct packed-data{ unsigned a:2; unsigned b:6; unsigned c:4; unsigned d:4; int I; }data; 其中a,b,c,d分别占2位,6位,4位,4位。I为整型,占4 个字节。 其中a、b、c、d分别占2位、6位、4位、4位。i为整型。共占4个字节。也可以使各个位段不恰好占满一个字节。如: struct packed-data {unsigned a∶2; unsigned b∶3; unsigned c∶4; int i; }; struct packed-data data; 其中a、b、c共占9位,占1个字节多,不到2个字节。它的后面为int型,占2个字节。在a、b、c之后7位空间闲置不用,i从另一字节开头起存放。 对于位段成员的引用如下: data.a = 2;等,但要注意赋值时,不要超出位段定义的范围。如位段成员a定义为2位,最大值为3,即(11)2,所以data.a=5;就会取5的两个低位进行赋值,就得不到想要的值了。 关于位段的定义和引用,有几点重要说明: ①若某一个段要从另一个字开始存放,可以定义: unsigned a:1; unsigned b:2; unsigned :0; unsigned c:3; (另一单元) 使用长度为0的位段,作用就是使下一个位段从下一个存储单元开始存放。 ②一个位段必须存放在用一个存储单元中,不能跨两个单元。 如: struct T { unsigned char a : 4; unsigned char b : 6; }; 结构T的成员a在一个存储单元中,b则在另一个存储单元中。 ③可以定义无名位段。如: unsigned a:1; unsigned :2; (这两位空间不用) unsigned b:3; ④位段的长度不能大于存储单元的长度,也不能定义位段数组。 2007.8.12 20:26 作者:wind 收藏 | 评论:0 不同编译器处理位段的差异 分类:默认栏目CCS里面认位段是从高位开始的,而Keil和凌阳单片机的编译器UNSP IDE以及NIOS II IDE,C++Builder里认位段是从低位开始的 同样做一个结构体,在Keil、UNSP IDE、NIOS II IDE、C++Builder里要这样: #define Uint unsigned int typedef struct { Uint bit0 : 1; Uint bit1 : 1; Uint bit2 : 1; Uint bit3 : 1; Uint bit4 : 1; Uint bit5 : 1; Uint bit6 : 1; Uint bit7 : 1; Uint bit8 : 1; Uint bit9 : 1; Uint bit10 : 1; Uint bit11 : 1; Uint bit12 : 1; Uint bit13 : 1; Uint bit14 : 1; Uint bit15 : 1; }Bit; 而在CCS里就应该这样: #define Uint unsigned int typedef struct { Uint bit15 : 1; Uint bit14 : 1; Uint bit13 : 1; Uint bit12 : 1; Uint bit11 : 1; Uint bit10 : 1; Uint bit9 : 1; Uint bit8 : 1; Uint bit7 : 1; Uint bit6 : 1; Uint bit5 : 1; Uint bit4 : 1; Uint bit3 : 1; Uint bit2 : 1; Uint bit1 : 1; Uint bit0 : 1; }Bit; 2007.8.12 17:21 作者:wind 收藏 | 评论:1 在ccs下建立一个工程的步骤 分类:默认栏目建立一个工程的步骤:1.在C:ti或任意目录下建立工程文件名如###.pjt 2.将dsp头文件拷到此文件夹下。将这些头文件添加到工程中,注意:有一个文件DSP281x_usDelay.asm特殊。由于它是asm文件,当在默认的文件类型下全选的时候可能不会选到它,要在文件类型列表里点All files然后全选。3。在工程下建立主函数C文件,并添加到工程中。4。添加F2812_EzDSP_RAM_lnk.cmd和DSP281x_Headers_nonBIOS.cmd文件 5。添加库文件在C:tic2000cgtoolslibrts2800_ml.6.编译运行。 如果出现下面的错误 undefined first referenced symbol in file --------- ---------------- _main C:tic2000cgtoolslibrts2800_ml.lib 》》 error: symbol referencing errors - ‘。/Debug/sci7.out’ not built Build Complete, 1 Errors, 0 Warnings, 0 Remarks. 这是因为缺少main 这个文件,只要将它添加到工程文件里! 2007.8.7 21:32 作者:wind 收藏 | 评论:0 DSP外部中断 分类:默认栏目要使用外部中断要从两方面进行准备:首先,在硬件上必须将中断信号引到DSP的外部中断引脚上。比如:FPGA的一个output发出中断请求信号那么就将这个管脚与DSP的XINT1相连,XINT1可以通过一个4.7k的电阻上拉到3.3v, 也可以不拉。其次是在软件上的配置:1.初始外部中断寄存器 void Xint1_init(void) { DINT; XIntruptRegs.XINT1CR.bit.ENABLE=1;//使能外部中断 XIntruptRegs.XINT1CR.bit.POLARITY=1;//中断产生在上升沿,若为0中断产生在下降沿 EINT; } 由于这是一个函数,所以也要有函数声明,函数调用,函数定义。 2 使能cpu INT1 中断 PieCtrlRegs.PIEIER1.bit.INTx4 = 1; //Enable all XINT1 interrupt IER |= 0x100; // Enable CPU INT IER |= 0x0001; // enable PIEIER1, and INT1 3.编写中断服务程序, interrupt void Xint1_ISR(void) { 。..。..。 PieCtrlRegs.PIEACK.all|=0x0001; } 4将中断服务程序入口地址赋给中断向量表 PieVectTable.XINT1=&Xint1_ISR; 总结起来使用外部中断的步骤如下: 1声明外部中断服务程序和外部中断初始化函数 void Xint1_init(void); interrupt void Xint1_ISR(void); 2将中断服务程序入口地址赋给中断向量表 PieVectTable.XINT1=&Xint1_ISR; 3使能cpu INT1 中断 IER |= 0x0001; // enable PIEIER1, and INT1 PieCtrlRegs.PIEIER1.bit.INTx4 = 1; //Enable all XINT1 interrupt 4. 在主函数体外定义专断初始化函数和中断服务程序 2007.8.7 21:30 作者:wind 收藏 | 评论:0 ti2812中用C语言来实现中断的说明 分类:默认栏目F2812中用C语言来实现中断的说明: 1.首先在.cmd中定位系统中断表: MEMORY { PAGE 0 : 。..。..。..。..。..。..。..。..。..。..。..。..。. PAGE 1 : 。..。..。..。..。..。..。..。..。..。..。..。..。. PIE_VECT : origin = 0x000D00, length = 0x000100 。..。..。..。..。..。..。..。..。..。..。..。..。. } SECTIONS { 。..。..。..。..。..。..。..。..。..。..。..。. PieVectTable : 》 PIE_VECT, PAGE = 1 。..。..。..。..。..。..。..。..。..。..。..。..。 } 2.在C中制定该中断的结构体: #pragma DATA_SECTION(PieVectTable,“PieVectTable”); struct PIE_VECT_TABLE PieVectTable;(在DSP28_GlobalVariableDefs.C中初始化) 3.用一组常数(按照中断向量的顺序)初始化该名字为PIE_VECT_TABLE的表: typedef interrupt void(*PINT)(void);这里有些一问,一下应该为函数名?? // Define Vector Table: struct PIE_VECT_TABLE { // Reset is never fetched from this table. // It will always be fetched from 0x3FFFC0 in either // boot ROM or XINTF Zone 7 depending on the state of // the XMP/MC input signal. On the F2810 it is always // fetched from boot ROM. PINT PIE1_RESERVED; PINT PIE2_RESERVED; PINT PIE3_RESERVED; PINT PIE4_RESERVED; PINT PIE5_RESERVED; PINT PIE6_RESERVED; PINT PIE7_RESERVED; PINT PIE8_RESERVED; PINT PIE9_RESERVED; PINT PIE10_RESERVED; PINT PIE11_RESERVED; PINT PIE12_RESERVED; PINT PIE13_RESERVED; // Non-Peripheral Interrupts: PINT XINT13; // XINT13 PINT TINT2; // CPU-Timer2 PINT DATALOG; // Datalogging interrupt PINT RTOSINT; // RTOS interrupt PINT EMUINT; // Emulation interrupt PINT XNMI; // Non-maskable interrupt PINT ILLEGAL; // Illegal operation TRAP PINT USER0; // User Defined trap 0 PINT USER1; // User Defined trap 1 PINT USER2; // User Defined trap 2 PINT USER3; // User Defined trap 3 PINT USER4; // User Defined trap 4 PINT USER5; // User Defined trap 5 PINT USER6; // User Defined trap 6 PINT USER7; // User Defined trap 7 PINT USER8; // User Defined trap 8 PINT USER9; // User Defined trap 9 PINT USER10; // User Defined trap 10 PINT USER11; // User Defined trap 11 // Group 1 PIE Peripheral Vectors: PINT PDPINTA; // EV-A PINT PDPINTB; // EV-B PINT rsvd1_3; PINT XINT1; PINT XINT2; PINT ADCINT; // ADC PINT TINT0; // Timer 0 PINT WAKEINT; // WD // Group 2 PIE Peripheral Vectors: PINT CMP1INT; // EV-A PINT CMP2INT; // EV-A PINT CMP3INT; // EV-A PINT T1PINT; // EV-A PINT T1CINT; // EV-A PINT T1UFINT; // EV-A PINT T1OFINT; // EV-A PINT rsvd2_8; // Group 3 PIE Peripheral Vectors: PINT T2PINT; // EV-A PINT T2CINT; // EV-A PINT T2UFINT; // EV-A PINT T2OFINT; // EV-A PINT CAPINT1; // EV-A PINT CAPINT2; // EV-A PINT CAPINT3; // EV-A PINT rsvd3_8; // Group 4 PIE Peripheral Vectors: PINT CMP4INT; // EV-B PINT CMP5INT; // EV-B PINT CMP6INT; // EV-B PINT T3PINT; // EV-B PINT T3CINT; // EV-B PINT T3UFINT; // EV-B PINT T3OFINT; // EV-B PINT rsvd4_8; // Group 5 PIE Peripheral Vectors: PINT T4PINT; // EV-B PINT T4CINT; // EV-B PINT T4UFINT; // EV-B PINT T4OFINT; // EV-B PINT CAPINT4; // EV-B PINT CAPINT5; // EV-B PINT CAPINT6; // EV-B PINT rsvd5_8; // Group 6 PIE Peripheral Vectors: PINT SPIRXINTA; // SPI-A PINT SPITXINTA; // SPI-A PINT rsvd6_3; PINT rsvd6_4; PINT MRINTA; // McBSP-A PINT MXINTA; // McBSP-A PINT rsvd6_7; PINT rsvd6_8; // Group 7 PIE Peripheral Vectors: PINT rsvd7_1; PINT rsvd7_2; PINT rsvd7_3; PINT rsvd7_4; PINT rsvd7_5; PINT rsvd7_6; PINT rsvd7_7; PINT rsvd7_8; // Group 8 PIE Peripheral Vectors: PINT rsvd8_1; PINT rsvd8_2; PINT rsvd8_3; PINT rsvd8_4; PINT rsvd8_5; PINT rsvd8_6; PINT rsvd8_7; PINT rsvd8_8; // Group 9 PIE Peripheral Vectors: PINT RXAINT; // SCI-A PINT TXAINT; // SCI-A PINT RXBINT; // SCI-B PINT TXBINT; // SCI-B PINT ECAN0INTA; // eCAN PINT ECAN1INTA; // eCAN PINT rsvd9_7; PINT rsvd9_8; // Group 10 PIE Peripheral Vectors: PINT rsvd10_1; PINT rsvd10_2; PINT rsvd10_3; PINT rsvd10_4; PINT rsvd10_5; PINT rsvd10_6; PINT rsvd10_7; PINT rsvd10_8; // Group 11 PIE Peripheral Vectors: PINT rsvd11_1; PINT rsvd11_2; PINT rsvd11_3; PINT rsvd11_4; PINT rsvd11_5; PINT rsvd11_6; PINT rsvd11_7; PINT rsvd11_8; // Group 12 PIE Peripheral Vectors: PINT rsvd12_1; PINT rsvd12_2; PINT rsvd12_3; PINT rsvd12_4; PINT rsvd12_5; PINT rsvd12_6; PINT rsvd12_7; PINT rsvd12_8; }; 然后在使我们在.cmd文件中定义的表有以上属性: extern struct PIE_VECT_TABLE PieVectTable;(在.h文件中) 4.初始化该表(在.c文件中)使之能够为主程序所使用: const struct PIE_VECT_TABLE PieVectTableInit = { PIE_RESERVED, // Reserved space PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, // Non-Peripheral Interrupts INT13_ISR, // XINT13 or CPU-Timer 1 INT14_ISR, // CPU-Timer2 DATALOG_ISR, // Datalogging interrupt RTOSINT_ISR, // RTOS interrupt EMUINT_ISR, // Emulation interrupt NMI_ISR, // Non-maskable interrupt ILLEGAL_ISR, // Illegal operation TRAP USER0_ISR, // User Defined trap 0 USER1_ISR, // User Defined trap 1 USER2_ISR, // User Defined trap 2 USER3_ISR, // User Defined trap 3 USER4_ISR, // User Defined trap 4 USER5_ISR, // User Defined trap 5 USER6_ISR, // User Defined trap 6 USER7_ISR, // User Defined trap 7 USER8_ISR, // User Defined trap 8 USER9_ISR, // User Defined trap 9 USER10_ISR, // User Defined trap 10 USER11_ISR, // User Defined trap 11 // Group 1 PIE Vectors PDPINTA_ISR, // EV-A PDPINTB_ISR, // EV-B rsvd_ISR, XINT1_ISR, XINT2_ISR, ADCINT_ISR, // ADC TINT0_ISR, // Timer 0 WAKEINT_ISR, // WD // Group 2 PIE Vectors CMP1INT_ISR, // EV-A CMP2INT_ISR, // EV-A CMP3INT_ISR, // EV-A T1PINT_ISR, // EV-A T1CINT_ISR, // EV-A T1UFINT_ISR, // EV-A T1OFINT_ISR, // EV-A rsvd_ISR, // Group 3 PIE Vectors T2PINT_ISR, // EV-A T2CINT_ISR, // EV-A T2UFINT_ISR, // EV-A T2OFINT_ISR, // EV-A CAPINT1_ISR, // EV-A CAPINT2_ISR, // EV-A CAPINT3_ISR, // EV-A rsvd_ISR, // Group 4 PIE Vectors CMP4INT_ISR, // EV-B CMP5INT_ISR, // EV-B CMP6INT_ISR, // EV-B T3PINT_ISR, // EV-B T3CINT_ISR, // EV-B T3UFINT_ISR, // EV-B T3OFINT_ISR, // EV-B rsvd_ISR, // Group 5 PIE Vectors T4PINT_ISR, // EV-B T4CINT_ISR, // EV-B T4UFINT_ISR, // EV-B T4OFINT_ISR, // EV-B CAPINT4_ISR, // EV-B CAPINT5_ISR, // EV-B CAPINT6_ISR, // EV-B rsvd_ISR, // Group 6 PIE Vectors SPIRXINTA_ISR, // SPI-A SPITXINTA_ISR, // SPI-A rsvd_ISR, rsvd_ISR, MRINTA_ISR, // McBSP-A MXINTA_ISR, // McBSP-A rsvd_ISR, rsvd_ISR, // Group 7 PIE Vectors rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, // Group 8 PIE Vectors rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, // Group 9 PIE Vectors SCIRXINTA_ISR, // SCI-A SCITXINTA_ISR, // SCI-A SCIRXINTB_ISR, // SCI-B SCITXINTB_ISR, // SCI-B ECAN0INTA_ISR, // eCAN ECAN1INTA_ISR, // eCAN rsvd_ISR, rsvd_ISR, // Group 10 PIE Vectors rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, // Group 11 PIE Vectors rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, // Group 12 PIE Vectors rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, }; //--------------------------------------------------------------------------- // InitPieVectTable: //--------------------------------------------------------------------------- // This function initializes the PIE vector table to a known state. // This function must be executed after boot time. // void InitPieVectTable(void) { int16 i; Uint32 *Source = (void *) &PieVectTableInit; Uint32 *Dest = (void *) &PieVectTable; EALLOW; for(i=0; i 《 128; i++) *Dest++ = *Source++; EDIS; // Enable the PIE Vector Table PieCtrl.PIECRTL.bit.ENPIE = 1; } 5.中断服务程序: 让以上的数值指向你所要的服务程序,例如: PieVectTable.TINT2 = &ISRTimer2; 那么,ISRTimer2也就成了中断服务程序, ×××切记:一定要在主程序的开始先声明该程序: interrupt void ISRTimer2(void); 。..。..。..。..。 。..。..。..。..。 然后按照您的需要编制该程序: interrupt void ISRTimer2(void) { CpuTimer2.InterruptCount++; } 2007.8.7 21:20 作者:wind 收藏 | 评论:0 关于F2812中用C语言来实现中断的说明 分类:默认栏目初始化该表(在.c文件中)使之能够为主程序所使用: const struct PIE_VECT_TABLE PieVectTableInit = { PIE_RESERVED, // Reserved space PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, PIE_RESERVED, // Non-Peripheral Interrupts INT13_ISR, // XINT13 or CPU-Timer 1 INT14_ISR, // CPU-Timer2 DATALOG_ISR, // Datalogging interrupt RTOSINT_ISR, // RTOS interrupt EMUINT_ISR, // Emulation interrupt NMI_ISR, // Non-maskable interrupt ILLEGAL_ISR, // Illegal operation TRAP USER0_ISR, // User Defined trap 0 USER1_ISR, // User Defined trap 1 USER2_ISR, // User Defined trap 2 USER3_ISR, // User Defined trap 3 USER4_ISR, // User Defined trap 4 USER5_ISR, // User Defined trap 5 USER6_ISR, // User Defined trap 6 USER7_ISR, // User Defined trap 7 USER8_ISR, // User Defined trap 8 USER9_ISR, // User Defined trap 9 USER10_ISR, // User Defined trap 10 USER11_ISR, // User Defined trap 11 // Group 1 PIE Vectors PDPINTA_ISR, // EV-A PDPINTB_ISR, // EV-B rsvd_ISR, XINT1_ISR, XINT2_ISR, ADCINT_ISR, // ADC TINT0_ISR, // Timer 0 WAKEINT_ISR, // WD // Group 2 PIE Vectors CMP1INT_ISR, // EV-A CMP2INT_ISR, // EV-A CMP3INT_ISR, // EV-A T1PINT_ISR, // EV-A T1CINT_ISR, // EV-A T1UFINT_ISR, // EV-A T1OFINT_ISR, // EV-A rsvd_ISR, // Group 3 PIE Vectors T2PINT_ISR, // EV-A T2CINT_ISR, // EV-A T2UFINT_ISR, // EV-A T2OFINT_ISR, // EV-A T2OFINT_ISR, // EV-A CAPINT1_ISR, // EV-A CAPINT2_ISR, // EV-A CAPINT3_ISR, // EV-A rsvd_ISR, // Group 4 PIE Vectors CMP4INT_ISR, // EV-B CMP5INT_ISR, // EV-B CMP6INT_ISR, // EV-B T3PINT_ISR, // EV-B T3CINT_ISR, // EV-B T3UFINT_ISR, // EV-B T3OFINT_ISR, // EV-B rsvd_ISR, // Group 5 PIE Vectors T4PINT_ISR, // EV-B T4CINT_ISR, // EV-B T4UFINT_ISR, // EV-B T4OFINT_ISR, // EV-B CAPINT4_ISR, // EV-B CAPINT5_ISR, // EV-B CAPINT6_ISR, // EV-B rsvd_ISR, // Group 6 PIE Vectors SPIRXINTA_ISR, // SPI-A SPITXINTA_ISR, // SPI-A rsvd_ISR, rsvd_ISR, MRINTA_ISR, // McBSP-A MXINTA_ISR, // McBSP-A rsvd_ISR, rsvd_ISR, // Group 7 PIE Vectors rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, // Group 8 PIE Vectors rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, // Group 9 PIE Vectors SCIRXINTA_ISR, // SCI-A SCITXINTA_ISR, // SCI-A SCIRXINTB_ISR, // SCI-B SCITXINTB_ISR, // SCI-B ECAN0INTA_ISR, // eCAN ECAN1INTA_ISR, // eCAN rsvd_ISR, rsvd_ISR, // Group 10 PIE Vectors rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, // Group 11 PIE Vectors rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, // Group 12 PIE Vectors rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, rsvd_ISR, }; //---------------------------------------------------------------------------// This function initializes the PIE vector table to a known state. // This function must be executed after boot time. // void InitPieVectTable(void) { int16 i; Uint32 *Source = (void *) &PieVectTableInit; Uint32 *Dest = (void *) &PieVectTable; EALLOW; for(i=0; i 《 128; i++) *Dest++ = *Source++; EDIS; // Enable the PIE Vector Table PieCtrl.PIECRTL.bit.ENPIE = 1; } 中断服务程序: 让以上的数值指向你所要的服务程序,例如: PieVectTable.TINT2 = &ISRTimer2; 那么,ISRTimer2也就成了中断服务程序, ×××切记:一定要在主程序的开始先声明该程序: interrupt void ISRTimer2(void); 。..。..。..。..。 。..。..。..。..。 然后按照您的需要编制该程序: interrupt void ISRTimer2(void) { CpuTimer2.InterruptCount++; } 2007.8.7 21:13 作者:wind 收藏 | 评论:0 #pragma 预处理指令详解 分类:默认栏目在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。 其格式一般为: #Pragma Para 其中Para 为参数,下面来看一些常用的参数。 (1)message 参数。 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗 口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为: #Pragma message(“消息文本”) 当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。 当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法 #ifdef _X86 #Pragma message(“_X86 macro activated!”) #endif 当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_ X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了 。 (2)另一个使用得比较多的pragma参数是code_seg。格式如: #pragma code_seg( [“section-name”[,“section-class”] ] ) 它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。 (3)#pragma once (比较常用) 只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。 (4)#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。 有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。 (5)#pragma resource “*.dfm”表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体 外观的定义。 (6)#pragma warning( disable : 4507 34; once : 4385; error : 164 ) 等价于: #pragma warning(disable:4507 34) // 不显示4507和34号警告信息 #pragma warning(once:4385) // 4385号警告信息仅报告一次 #pragma warning(error:164) // 把164号警告信息作为一个错误。 同时这个pragma warning 也支持如下格式: #pragma warning( push [ ,n ] ) #pragma warning( pop ) 这里n代表一个警告等级(1---4)。 #pragma warning( push )保存所有警告信息的现有的警告状态。 #pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告 等级设定为n。 #pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的 一切改动取消。例如: #pragma warning( push ) #pragma warning( disable : 4705 ) #pragma warning( disable : 4706 ) #pragma warning( disable : 4707 ) //。..。..。 #pragma warning( pop ) 在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。 (7)pragma comment(。..) 该指令将一个注释记录放入一个对象文件或可执行文件中。 常用的lib关键字,可以帮我们连入一个库文件。 每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。例如,对循环优化功能: #pragma loop_opt(on) // 激活 #pragma loop_opt(off) // 终止 有时,程序中会有些函数会使编译器发出你熟知而想忽略的警告,如“Parameter xxx is never used in function xxx”,可以这样: #pragma warn —100 // Turn off the warning message for warning #100 int insert_record(REC *r) { /* function body */ } #pragma warn +100 // Turn the warning message for warning #100 back on 函数会产生一条有唯一特征码100的警告信息,如此可暂时终止该警告。 每个编译器对#pragma的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。 2007.8.5 15:37 作者:wind 收藏 | 评论:0 32位RISC CPU ARM芯片的应用和选型 分类:默认栏目摘要:ARM公司以及ARM芯片的现状和发展,从应用的角度介绍了ARM芯片的选择方法,并介绍了具有多芯核结构的ARM芯片。列举了目前的主要ARM芯片供应商,其产品以及应用领域。举例说明了几种嵌入式产品的最佳ARM芯片选择方案。 关键词:ARM MMU SOC RISC CPU ARM公司自1990年正式成立以来, 在32位RISC (Reduced Instruction Set Computer CPU开发领域不断取得突破,其结构已经从V3发展到V6。由于ARM公司自成立以来,一直以IP(Intelligence Property)提供者的身份向各大半导体制造商出售知识产权,而自己从不介入芯片的生产销售,加上其设计的芯核具有功耗低、成本低等显著优点,因此获得众多的半导体厂家和整机厂商的大力支持,在32位嵌入式应用领域获得了巨大的成功,目前已经占有75%以上的32位RISC嵌入式产品市场。在低功耗、低成本的嵌入式应用领域确立了市场领导地位。现在设计、生产ARM芯片的国际大公司已经超过50多家,国内中兴通讯和华为通讯等公司也已经购买ARM公司的芯核用于通讯专用芯片的设计。 目前非常流行的ARM芯核有ARM7TDMI,StrongARM ARM720T,ARM9TDMI,ARM922T,ARM940T,RM946T, ARM966T,ARM10TDM1等。自V5以后,ARM公司提供Piccolo DSP的芯核给芯片设计者,用于设计ARM+DSP 的SOC (System On Chip) 结构的芯片。此外,ARM芯片还获得了许多实时操作系统(Real Time Operating System)供应商的支持,比较知名的有:Windows CE、Linux、pSOS、VxWorks Mucleus、EPOC、uCOS、BeOS等。 随着国内嵌入式应用领域的发展,ARM芯片必然会获得广泛的重视和应用。但是,由于ARM芯片有多达十几种的芯核结构,70多家芯片生产厂家,以及千变万化的内部功能配置组合,给开发人员在选择方案时带来一定的困难。所以,对ARM芯片做一对比研究是十分必要的。 1 ARM芯片选择的一般原则 从应用的角度,对在选择ARM芯片时所应考虑的主要因素做一详细的说明。 1.1 ARM芯核 如果希望使用WinCE或Linux等操作系统以减少软件开发时间,就需要选择ARM720T以上带有MMU(memory management unit)功能的ARM芯片,ARM720T、StrongARM、ARM920T、ARM922T、ARM946T都带有MMU功能。而ARM7TDMI没有MMU,不支持Windows CE和大部分的Linux, 但目前有uCLinux等少数几种Linux不需要MMU的支持。 1.2 系统时钟控制器 系统时钟决定了ARM芯片的处理速度。ARM7的处理速度为0.9MIPS/MHz,常见的ARM7芯片系统主时钟为20MHz-133MHz,ARM9的处理速度为1.1MIPS/MHz,常见的ARM9的系统主时钟为100MHz-233MHz, ARM10最高可以达到700MHz。不同芯片对时钟的处理不同,有的芯片只有一个主时钟频率,这样的芯片可能不能同时顾及UART和音频时钟的准确性,如Cirrus Logic的EP7312等;有的芯片内部时钟控制器可以分别为CPU核和USB、UART、DSP、音频等功能部件提供不同频率的时钟,如PHILIPS公司的SAA7550等芯片。 1.3 内部存储器容量 在不需要大容量存储器时,可以考虑选用有内置存储器的ARM芯片。见表1。 表1 内置存储器的ARM芯片 芯片型号 供应商 FLASH容量 ROM容量 SRAM容量 AT91F40162 ATMEL 2M Bytes 256K bytes 4K Bytes AT91FR4081 ATMEL 1M Bytes 128K Bytes SAA7750 Philips 384K Bytes 64K bytes PUC3030A Micronas 256K Bytes 56K bytes HMS30C7202 Hynix 192K Bytes ML67Q4001 OKI 256K Bytes LC67F500 Snayo 640K Bytes 32K 1.4 USB接口 许多ARM芯片内置有USB控制器,有些芯片甚至同时有USB Host和USB Slave控制器。见表2。 表2 内置USB控制器的ARM芯片 芯片型号 ARM内核 供应商 USB Slave USB Host IIS接口 S3C2410 ARM920T Samsung 1 2 1 S3C2400 ARM920T Samsung 1 2 1 S5N8946 ARM7TDMI samsung 1 0 0 L7205 ARM720T Linkup 1 1 0 L7210 ARM720T Linkup 1 1 0 EP9312 ARM920T Cirrus Logic0 3 1 Dragonball MX1 ARM920T Motorola 1 0 1 SAA7750 ARM720T Philips 1 0 1 TMS320DSC2x ARM7TDMI TI 1 0 0 PUC3030A ARM7TDMI Micronas 1 0 5 AAEC-2000 ARM920T Agilent 1 0 0 ML67100 ARM7TDMI OKI 1 0 0 ML7051LA ARM7TDMI OKI 0 0 SA-1100 StrongARM Intel 1 0 0 LH79531 ARM7TDMI Sharp 1 0 0 GMS320C7201 ARM720T Hynix 1 0 1 1.5 GPIO数量 在某些芯片供应商提供的说明书中,往往申明的是最大可能的GPIO数量,但是有许多引脚是和地址线、数据线、串口线等引脚复用的。这样在系统设计时需要计算实际可以使用的GPIO数量。 1.6 中断控制器 ARM内核只提供快速中断(FIQ)和标准中断(IRQ)两个中断向量。但各个半导体厂家在设计芯片时加入了自己不同的中断控制器,以便支持诸如串行口、外部中断、时钟中断等硬件中断。外部中断控制是选择芯片必须考虑的重要因素,合理的外部中断设计可以很大程度的减少任务调度的工作量。例如PHILIPS公司的SAA7750,所有GPIO都可以设置成FIQ或IRQ,并且可以选择上升沿、下降沿、高电平、低电平四种中断方式。这使得红外线遥控接收、指轮盘和键盘等任务都可以作为背景程序运行。而Cirrus Logic公司的EP7312芯片,只有4个外部中断源,并且每个中断源都只能是低电平或者高电平中断,这样在用于接收红外线信号的场合时,就必须用查询方式,会浪费大量的CPU时间。 1.7 IIS(Integrate Interface of Sound)接口 即集成音频接口。如果设计音频应用产品,IIS 总线接口是必需的。 1.8 nWAIT信号 外部总线速度控制信号。不是每个ARM芯片都提供这个信号引脚,利用这个信号与廉价的GAL芯片就可以实现与符合PCMCIA标准的WLAN卡和Bluetooth卡的接口,而不需要外加高成本的PCMCIA专用控制芯片。另外,当需要扩展外部DSP 协处理器时,此信号也是必需的。 1.9 RTC (Real Time Clock) 很多ARM芯片都提供实时时钟功能,但方式不同。如Cirrus Logic公司的EP7312的RTC只是一个32位计数器,需要通过软件计算出年月日时分秒;而SAA7750和S3C2410等芯片的RTC直接提供年月日时分秒格式。 1.10 LCD控制器 有些ARM芯片内置LCD控制器,有的甚至内置64K彩色TFT LCD控制器。在设计PDA和手持式显示记录设备时,选用内置LCD控制器的ARM芯片如S1C2410较为适宜。 1.11 PWM输出 有些ARM芯片有2~8路PWM输出,可以用于电机控制或语音输出等场合。 1.12 ADC和DAC 有些ARM芯片内置2~8通道8~12位通用ADC,可以用于电池检测、触摸屏和温度监测等。PHILIPS的SAA7750更是内置了一个16位立体声音频ADC和DAC,并且带耳机驱动。 1.13 扩展总线 大部分ARM芯片具有外部SDRAM和SRAM扩展接口,不同的ARM芯片可以扩展的芯片数量即片选线数量不同,外部数据总线有8位、16位或32位。某些特殊应用的ARM芯片如德国Micronas的PUC3030A没有外部扩展功能。 1.14 UART和IrDA 几乎所有的ARM芯片都具有1~2个UART接口,可以用于和PC机通讯或用Angel 进行调试。一般的ARM芯片通讯波特率为115,200bps,少数专为蓝牙威廉希尔官方网站 应用设计的ARM芯片的UART通讯波特率可以达到920Kbps,如Linkup 公司的L7205。 1.15 DSP协处理器,见表3。 表3 ARM+DSP结构的ARM芯片 芯片型号 供应商 DSP core DSP MIPS 应用 TMS320DSC2X TI 16bits C5000 500 Digital Camera Dragonball MX1 Motorola 24bits 56000 CD-MP3 SAA7750 Philips 24bits EPIC 73 CD-MP3 VWS22100 Philips 16bits OAK 52 GSM STLC1502 ST D950 VOIP GMS30C3201 Hynix 16bits Piccolo STB AT75C220 ATMEL 16bits OAK 40 IA AT75C310 ATMEL 16bits OAK 40x2 IA AT75C320 ATMEL 16bits OAK 60X2 IA L7205 Linkup 16bits Piccolo Wireless L7210 Linkup 16bits Piccolo wireless Quatro OAK 16bits OAK Digital Image 1.16 内置FPGA 有些ARM芯片内置有FPGA,适合于通讯等领域。见表4。 表4 ARM+FPGA结构的ARM芯片 芯片型号 供应商 ARM芯核 FPGA门数 引脚数 EPXA1 Altera ARM922T 100K 484 EPXA4 Altera ARM922T 400K 672 EPXA10 Altera ARM922T 1000K 1020 TA7S20系列 Triscend ARM7TDMI 多种 多种 1.17 时钟计数器和看门狗 一般ARM芯片都具有2~4个16位或32位时钟计数器和一个看门狗计数器。 1.18 电源管理功能 ARM芯片的耗电量与工作频率成正比,一般ARM芯片都有低功耗模式、睡眠模式和关闭模式。 1.19 DMA控制器 有些ARM芯片内部集成有DMA(Direct Memory Access)可以和硬盘等外部设备高速交换数据,同时减少数据交换时对CPU资源的占用。 另外,还可以选择的内部功能部件有:HDLC, SDLC,CD-ROM Decoder,Ethernet MAC,VGA controller, DC-DC。可以选择的内置接口有:IIC,SPDIF,CAN,SPI,PCI,PCMCIA。 最后需说明的是封装问题。ARM芯片现在主要的封装有QFP、TQFP、PQFP、LQFP、BGA、LBGA等形式,BGA封装具有芯片面积小的特点,可以减少PCB板的面积,但是需要专用的焊接设备,无法手工焊接。另外一般BGA封装的ARM芯片无法用双面板完成PCB布线,需要多层PCB板布线。 2 多芯核结构ARM芯片的选择 为了增强多任务处理能力、数学运算能力、多媒体以及网络处理能力,某些供应商提供的ARM芯片内置多个芯核,目前常见的有ARM+DSP,ARM+FPGA,ARM+ARM等结构。 2.1多ARM芯核 为了增强多任务处理能力和多媒体处理能力,某些ARM芯片内置多个ARM芯核。例如Portal player 公司的PP5002 内部集成了两个ARM7TDMI 芯核,可以应用于便携式MP3播放器的编码器或解码器。从科胜讯公司(Conexant)分离出去的专门致力于高速通讯芯片设计生产的MinSpeed公司就在其多款高速通讯芯片中集成了2~4个ARM7TDMI内核。 2.2 ARM芯核+DSP芯核 为了增强数学运算功能和多媒体处理功能,许多供应商在其ARM芯片内增加了DSP协处理器。通常加入的DSP芯核有ARM公司的Piccolo DSP芯核、OAK公司16位定点DSP芯核、TI的TMS320C5000系列DSP芯核、Motorola的56K DSP芯核等。见表3。 2.3 ARM芯核+FPGA 为了提高系统硬件的在线升级能力,某些公司在ARM芯片内部集成了FPGA。见表4。 3 主要ARM芯片供应商 目前可以提供ARM芯片的著名欧美半导体公司有:英特尔、德洲仪器、三星半导体、摩托罗拉、飞利浦半导体、 意法半体、亿恒半导体、科胜讯、ADI公司、安捷伦、高通公司、Atmel、Intersil、Alcatel、Altera、Cirrus Logic、Linkup、Parthus、LSI logic、Micronas, Silicon Wave、Virata、Portalplayer inc.、NetSilicon,Parthus。见表5。日本的许多著名半导体公司如东芝、三菱半导体、爱普生、富士通半导体、松下半导体等公司较早期都大力投入开发了自主的32位CPU结构,但现在都转向购买ARM公司的芯核进行新产品设计。由于它们购买ARM版权较晚,现在还没有可以销售的ARM芯片,而OKI、NEC、AKM、OAK、Sharp、Sanyo、Sony、Rohm等日本半导体公司目前都已经批量生产了ARM芯片。韩国的现代半导体公司也生产提供ARM芯片。另外,国外也有很多设备制造商采用ARM公司的芯核设计自己的专用芯片,如美国的IBM、3COM和新加坡的创新科技等。我国***地区可以提供ARM芯片的公司有台积电、台联电、华帮电子等。其它已购买ARM芯核,正在设计自主版权专用芯片的大陆公司有华为通讯和中兴通讯等。 表5 主要ARM芯片供应商及其代表性产品和主要应用领域 供应商 芯片1 芯片2 芯片3 芯片4 主要应用 Intel SA-110 SA-1100 SA-1110 IXP1200 Palm PC, Network TI TMS320DSC21 TMS320DSC24 TMS320DSC25 OMAP1510 Digital Camera Samsung S3C44B0X S3C2410 S3C4510 S5N8946 ADSL,PDA Motorola Dragonball MX1 BT,PDA Philips SAA7750 VWS22100 VCS94250 VWS26001 MP3,GSM ,3G,BT Cirrus Logic EP7209 EP7212 EP7312 EP9312 GP,MP3 Linkup L7200 L7205 L7210 Wireless ATMEL AT91R40XXX AT75C310 AT76C901 AT76C502 GP, Wireless OKI ML67100 ML7051LA ML67Q4000 ML67Q2300 GP,BT Sharp LH75400/1 LH79520LH79520 LH79531/2/3 LH7A400 Portable handheld Qualcomm MSP1000 MSM3000 MSM5000 MSM6000 CDMA ST STLC1502 STw2400 VOIP,BT Infineon PMB7754 BT Analog AD20MSP430 GSM Hynix GMS30C7201 HMS30C7202 HMS39C7092 STB,GP Micronas PUC3030A GP, MP3 Conexant CN9414 CX82100 Network, Modem Agilent AAEC-2000 IA Portalplayer PP5002 MP3, PDA NEC UPD65977 Configurable NetSilicon NET+15 NET+40 NET+50 PDA, Phone LSI Logic CBP3.0 CBP4.0 L64324 BT Alcatel MTC20276 MTK20141 MTK20285 MTC20277 Digital Image Altera EPXA1 EPXA4 EPXA10 Configurable Panasonic MN1A7T0200 PDA, Phone Silicon WaveSiW1750 BT OAK Quatro Digital Image Rohm BU6611AKU ISDN Parthus InfoSream Wireless Internet Intersil ISL3856 802.11b, WLAN SiRF SiRF Star II GPS Sirius CDMAx DIRAC 3G CDMA Sanyo VOL101 CD-R, HDC Virata Helium Helium 200 Helium 210 Lithium Communications Agere T8300 T8302 Mobile phone 表6 最佳应用方案推荐 应用 第一选择方案 第二方案 注释 高档PDA S3C2410 Dragon ball MX1 便携式CDMP3播放器 SAA7750 USB和CD-ROM解码器 FLASH MP3播放器 SAA7750 PUC3030A 内置USB和FLASH WLAN和BT应用产品 L7205,L7210 Dragon ball MX1 高速串口和PCMCIA接口 Voice Over IP STLC1502 数字式照相机 TMS320DSC24 TMS320DSC21 内置高速图像处理DSP 便携式语音email机 AT75C320 AT75C310 内置双DSP,可以分别处理MODEM和语音 GSM手机 VWS22100 AD20MSP430 专为GSM手机开发 ADSL Modem S5N8946 MTK-20141 电视机顶盒 GMS30C3201 VGA控制器 3G移动电话机 MSM6000 OMAP1510 10G光纤通信 MinSpeed公司系列ARM芯片 多ARM核+多DSP核 4 选择方案举例 表6列举的最佳方案仅供参考,由于SOC集成电路的发展非常迅速,今天的最佳方案到明天就可能不是最佳的了。因此任何时候在选择方案时,都应广泛搜寻一下主要的ARM芯片供应商,以找出最适合的芯片。 参考文献 1 Steve Furber. ARM System-on-chip Architecture Addison Wesley,2000 2007.7.25 14:18 作者:wind 收藏 | 评论:0 c语言中的精确延时程序举例 分类:默认栏目我在网上到看了一些关于延时的讨论,其中有篇文章 51单片机 Keil C 延时程序的简单研究,作者:InfiniteSpace Studio/isjfk 写得不错,他是用while(--i);产生DJNZ 来实现精确延时,后来有人说如果while里面不能放其它语句,否则也不行,用do-while就可以,具体怎样我没有去试。所有这些都没有给出具体的实例程序来。还看到一些延时的例子多多少少总有点延时差。为此我用for循环写了几个延时的子程序贴上来,希望能对初学者有所帮助。(晶振12MHz,一个机器周期1us.) 一。 500ms延时子程序 程序: void delay500ms(void) { unsigned char i,j,k; for(i=15;i》0;i--) for(j=202;j》0; j--) for(k=81;k》0;k--); } 产生的汇编: C:0x0800 7F0F MOV R7,#0x0F C:0x0802 7ECA MOV R6,#0xCA C:0x0804 7D51 MOV R5,#0x51 C:0x0806 DDFE DJNZ R5,C:0806 C:0x0808 DEFA DJNZ R6,C:0804 C:0x080A DFF6 DJNZ R7,C:0802 C:0x080C 22 RET 计算分析: 程序共有三层循环 一层循环n:R5*2 = 81*2 = 162us DJNZ 2us 二层循环m:R6*(n+3) = 202*165 = 33330us DJNZ 2us + R5赋值 1us = 3us 三层循环: R7*(m+3) = 15*33333 = 499995us DJNZ 2us + R6赋值 1us = 3us 循环外: 5us 子程序调用 2us + 子程序返回 2us + R7赋值 1us = 5us 延时总时间 = 三层循环 + 循环外 = 499995+5 = 500000us =500ms 计算公式:延时时间=[(2*R5+3)*R6+3]*R7+5 二。 200ms延时子程序 程序: void delay200ms(void) { unsigned char i,j,k; for(i=5;i》0;i--) for(j=132;j》0;j--) for(k=150;k》0;k--); } 产生的汇编 C:0x0800 7F05 MOV R7,#0x05 C:0x0802 7E84 MOV R6,#0x84 C:0x0804 7D96 MOV R5,#0x96 C:0x0806 DDFE DJNZ R5,C:0806 C:0x0808 DEFA DJNZ R6,C:0804 C:0x080A DFF6 DJNZ R7,C:0802 C:0x080C 22 RET 三。 10ms延时子程序 程序: void delay10ms(void) { unsigned char i,j,k; for(i=5;i》0;i--) for(j=4;j》0;j--) for(k=248;k》0;k--); } 产生的汇编 C:0x0800 7F05 MOV R7,#0x05 C:0x0802 7E04 MOV R6,#0x04 C:0x0804 7DF8 MOV R5,#0xF8 C:0x0806 DDFE DJNZ R5,C:0806 C:0x0808 DEFA DJNZ R6,C:0804 C:0x080A DFF6 DJNZ R7,C:0802 C:0x080C 22 RET 四。 1s延时子程序 程序: void delay1s(void) { unsigned char h,i,j,k; for(h=5;h》0;h--) for(i=4;i》0;i--) for(j=116;j》0;j--) for(k=214;k》0;k--); } 产生的汇编 C:0x0800 7F05 MOV R7,#0x05 C:0x0802 7E04 MOV R6,#0x04 C:0x0804 7D74 MOV R5,#0x74 C:0x0806 7CD6 MOV R4,#0xD6 C:0x0808 DCFE DJNZ R4,C:0808 C:0x080A DDFA DJNZ R5,C:0806 C:0x080C DEF6 DJNZ R6,C:0804 C:0x080E DFF2 DJNZ R7,C:0802 C:0x0810 22 RET 在精确延时的计算当中,最容易让人忽略的是计算循环外的那部分延时,在对时间要求不高的场合,这部分对程序不会造成影响。 2007.7.16 20:04 作者:wind 收藏 | 评论:0 电容测试(头文件) 分类:默认栏目/* ZLG7289.h 数码管显示与键盘管理芯片ZLG7289 的标准80C51 驱动程序头文件 Copyright (c) 2005,广州周立功单片机发展有限公司 All rights reserved. 本程序仅供学习参考,不提供任何可靠性方面的担保;请勿用于商业目的。 */ #ifndef _ZLG7289_H_ #define _ZLG7289_H_ #include 《REG52.h》 //定义I/O 接口 ***it ZLG7289_pinCS = P1^3; //片选信号,低电平有效 ***it ZLG7289_pinCLK = P1^1; //时钟信号,上升沿有效 ***it ZLG7289_pinDIO = P1^0; //数据信号,双向 ***it ZLG7289_pinINT = P3^2; //键盘中断请求信号,低电平(负边沿)有效 ***it ZLG7289_pinCS0 = P1^2; //执行ZLG7289 纯指令 extern void ZLG7289_cmd(char cmd); //执行ZLG7289 带数据指令 extern void ZLG7289_cmd_dat(char cmd, char dat); //以下是ZLG7289 的用户指令集 //复位(清除)指令 #define ZLG7289_Reset() ZLG7289_cmd(0xA4) #define ZLG7289_Reset0() ZLG7289_cmd0(0xA4) //测试指令 #define ZLG7289_Test() ZLG7289_cmd(0xBF) #define ZLG7289_Test0() ZLG7289_cmd0(0xBF) //左移指令 #define ZLG7289_SHL() ZLG7289_cmd(0xA0) //右移指令 #define ZLG7289_SHR() ZLG7289_cmd(0xA1) //循环左移指令 #define ZLG7289_ROL() ZLG7289_cmd(0xA2) //循环右移指令 #define ZLG7289_ROR() ZLG7289_cmd(0xA3) //下载数据 extern void ZLG7289_Download(unsigned char mod, char x, bit dp, char dat) ; extern void ZLG7289_Download0(unsigned char mod, char x, bit dp, char dat); //闪烁控制 // x 的8 个位分别对应数码管的8 个位,0-闪烁,1-不闪烁 #define ZLG7289_Flash(x) ZLG7289_cmd_dat(0x88,(x)) //消隐控制 // x 的8 个位分别对应数码管的8 个位,0-消隐,1-显示 #define ZLG7289_Hide(x) ZLG7289_cmd_dat(0x98,(x)) //段点亮指令 //seg=0~63,8 只数码管被看成64 只独立的LED //每只数码管中各段的点亮顺序按照“g,f,e,d,c,b,a,dp”进行 #define ZLG7289_SegOn(seg) ZLG7289_cmd_dat(0xE0,(seg)) //段关闭指令 //seg=0~63,8 只数码管被看成64 只独立的LED //每只数码管中各段的熄灭顺序按照“g,f,e,d,c,b,a,dp”进行 #define ZLG7289_SegOff(seg) ZLG7289_cmd_dat(0xC0,(seg)) //执行ZLG7289 键盘命令 extern char ZLG7289_Key() reentrant; //ZLG7289 初始化 extern void ZLG7289_Init(unsigned char t); #endif //_ZLG7289_H_ 2007.7.15 10:27 作者:wind 收藏 | 评论:0 电容测试程序1 分类:默认栏目/* ZLG7289Demo2.c 说明: 采用中断方式读取按键值 保存最后一次值 */ #include 《absacc.h》 #include “ZLG7289.h” #include《stdio.h》 #include《intrins.h》 #define uchar unsigned char #define uint unsigned int #define ulong unsigned long /***************************************************/ /*************************************************/ ***it clk= P2^7; //ad_clkAD转换 ***it adcs= P1^6; //ad_cs ***it dout= P3^5; //ad_dout ***it adcs0 = P1^5; //ad_cs ***it PRINTLED=P1^7;//led ***it JIANCE=P3^4;///;;光藕检测系统运行位为0时开始检测 ***it HC154A=P2^1; ***it HC154B=P2^2; ***it HC154C=P2^3; ***it HC154D=P2^4; ***it HC154G=P2^5; ***it ACC_0=ACC^0; ***it ACC_1=ACC^1; ***it ACC_2=ACC^2; ***it ACC_3=ACC^3; ***it ACC_4=ACC^4; //***it p1_4=P1^4;//外部中断输出用于做可变支流电源 /*************************************************** //DA转换 ***it SPI_DATA=P3^5; ***it SPI_CLK=P2^7; ***it CS_DA=P1^4; /*************************************************/ //bit定义位,两个字节 uchar bdata flag1; uchar bdata flag2; ***it SHEDING=flag1^0;//设定标志,为1手动,为0自动 ***it RUN=flag1^1;//运行标志 ***it PRINT=flag1^2;//打印标志,只有在运行时候草可以打印 ***it DUQUE=flag2^0;//上电读取SHEDING位。显示手动或自动 ***it XUN_F_BIAOZHI=flag1^4;// ***it SAVEXS_BIAOZHI=flag1^5;// ***it YCSD=flag1^6;//没用到 ***it dp=flag1^7;//7289小数位 /************************************************/ //定义一个全局变量,用于延时 //该变量在调用函数ZLG7289_Init()时被初始化 /*unsigned char ZLG7289_Delay_t; /* 宏定义:ZLG7289_ShortDelay() 功能:短延时 说明:延时(ZLG7289_Delay_t*2+2)个机器周期 */ /*#define ZLG7289_ShortDelay() { unsigned char t = ZLG7289_Delay_t; while ( --t != 0 ); } /* 宏定义:ZLG7289_LongDelay() 功能:长延时 说明:延时(ZLG7289_Delay_t*12+8)个机器周期 */ /*#define ZLG7289_LongDelay() { unsigned char t = ZLG7289_Delay_t * 6; while ( --t != 0 ); }*/ //说明:定义一个联合结构变量,用于保存打印测试电容值 union { ulong printc; struct{ uchar a1; uchar a2; uchar a3; uchar a4; }a; }c; /*************************************************/ #define TM_SEC XBYTE[0xFE00] //秒 #define TM_MIN XBYTE[0xFE02] //分 #define TM_HOU XBYTE[0xFE04] //时 #define DATE XBYTE[0xFE07] //天 #define MONTH XBYTE[0xFE08] //月 #define YEAR XBYTE[0xFE09] //年 #define AM_SEC XBYTE[0xFE01] //秒报警 #define AM_MIN XBYTE[0xFE03] #define AM_HOU XBYTE[0xFE05] #define REG_A XBYTE[0xFE0a] //A #define REG_B XBYTE[0xFE0b]//B #define REG_C XBYTE[0xFE0c]//C #define REG_D XBYTE[0xFE0d]//D #define TIME_M XBYTE[0xFE0f]//DS12887S存储查表标志位。即程序中M值 #define TIME_FLAG1 XBYTE[0xFE10]//存储自动,手动 标志位;;;可以不用 #define C_ROMLL XBYTE[0xFE11]//存储电容小数位 #define C_ROML XBYTE[0xFE12]//存储电容低位 #define C_ROMH XBYTE[0xFE13]//存储电容高位位 #define printv1 XBYTE[0xFE14]//存储测试电压最低位 #define printv2 XBYTE[0xFE15]//存储测试电压次低位 #define printv3 XBYTE[0xFE16]//存储测试电压高位 #define printv4 XBYTE[0xFE17]//存储测试电压最高位 /*******************************************************************/ //void Set_calendar(void);//设置时间 void Read_calendar(void);//读时钟 //void Start_calendar(void);//启动始终 void Display_calendar(void);//显示时间 //void Stop_calendar(void);//停止时钟 void Print_Byte(uchar ch);//打印一个字节 void Print_String(uchar *str);//打印字符 uint adc0(void); //ad 电流转换 convert uint adc(void); //ad u convert 电压转换 void delay_ad(uchar us); //delay //the result of ad convert void autoflashdisplay (void);//自动闪烁显示//可以不用 void autounflashdisplay (void);//自动不闪烁显示 void handflashdisplay (void);//手动闪烁显示 void handunflashdisplay (void);//手动不闪烁显示 void Delay(unsigned int t);///延时 void addisplay (void);///ad u//电压转换显示 void ad0display (void);//ad i电流转换显示 /*******************************************************************/ uint result=0; //存电压显示值 uint result0=0; //存电流显示值 uint printv;//存打印电压值 float u1=0;//电压转换 float u2=0;//电流转换 ulong resultc=0;//计算的电容值 ulong prc=0;//打印的电容值 uchar xdata *calendar_address=0xFE00;//时钟地址外部数据存储器 /*******************************************************/ uchar calendar_time[7]={07,07,14,15,35,00,6}; /* 05/8/16 14:58:00 星期二 */ code uchar at[7]={9,8,7,4,2,0,6}; /* 年、月、日、时、分、秒 、星期*/ float ad[2]={0,0};//定义两个数组,保存电容计算时电压与电流值 uchar dat; uchar d; uchar x; uchar m; uchar temp; uchar c1,c2,c3,c4,c5,c6;//用于打印测试电容 uchar autodat[6]={0x77,0x01,0x01,0x01,0x01,0x01};//自动显示 code uchar dangwei[11][6]={{0x00,0x00,0x5B,0xFE,0x7E,0x7E},{0x00,0x30,0x7E,0xFE,0x7E,0x7E}, {0x00,0x30,0x5B,0xFE,0x7E,0x7E},{0x00,0x6D,0x7E,0xFE,0x7E,0x7E}, {0x00,0x6D,0x5B,0xFE,0x7E,0x7E},{0x00,0x79,0x7E,0xFE,0x7E,0x7E}, {0x00,0x33,0x7E,0xFE,0x7E,0x7E}, {0x00,0x5B,0x7E,0xFE,0x7E,0x7E},{0x00,0x70,0x7E,0xFE,0x7E,0x7E}, {0x00,0x7F,0x7E,0xFE,0x7E,0x7E},{0x30,0x7E,0x7E,0xFE,0x7E,0x7E}};//电容挡位设置 /*******************************************************/ //定义全局变量Key,用来保存按键值 /* 功能:ZLG7289 键盘中断服务程序 参数: 读到的键盘值放在全局变量Key 中 说明: 中断触发方式要设置成负边沿触发 */ volatile unsigned char Key = 0xFF; //0xFF 表示未按键的状态 /***************************************************/ /******************************************************************* void Set_calendar(void) //设置系统时间 { unsigned char i; REG_B=0x82; //关闭时钟 for(i=0;i《7;i++) *(calendar_address+at)=calendar_time;//at[7]={9,8,7,4,2,0,6}; /* 年、月、日、时、分、秒 、星期calendar_time[7]={05,8,16,18,58,55,2}; 05/8/16 18:58:00 星期二 } /***********************************************************************/ void setprint(void) { TMOD=0x20; // 定时器1工作于方式2 // 串口工作于方式1 TH1=0xfD; //装入初值,11.0592MHZ的晶振,9600的波特率 TL1=0xfD; TR1=1; SCON=0x40; //PCON=0x00; //波特率无倍增 TI=1; //开启定时器1开始工作 } /****************************************************************/ void Read_calendar(void) //读取系统时间 { unsigned char i; REG_B=0x06;//0X06 0x20 do{ temp=REG_A;} //读积存器A while(temp&0x80);//为1时间更新很快将要发生。既为1时一直读并判断 for(i=0;i《7;i++) calendar_time=*(calendar_address+at); ///读时间 } void Start_calendar(void) //启动时钟%% { unsigned char temp; REG_A=0x20; temp=REG_C; temp=REG_D; REG_B=0x06; } /***************************************************************** void Stop_calendar(void)//停止时钟 { REG_A=0x70; } *******************************************************************/ /**************************************************************** 时钟显示 void Display_calendar(void)//下排7289 { dat = (calendar_time[5]%10);//秒 ZLG7289_Download0(1,0,0,dat); dat =(calendar_time[5]/10); ZLG7289_Download0(1,1,0,dat); dat = (calendar_time[4]%10);//分 ZLG7289_Download0(1,2,0,dat); dat =(calendar_time[4]/10); ZLG7289_Download0(1,3,0,dat); dat=(calendar_time[3]%10); ZLG7289_Download0(1,4,0,dat);//时 dat =(calendar_time[3]/10); ZLG7289_Download0(1,5,0,dat); } ************************************************************/ void addisplay (void)//电压显示 { dat=result%10;// ZLG7289_Download0(1,0,0,dat);//译码方式1 dat=result/1000; ZLG7289_Download0(1,3,0,dat); result=result/10; dat=result%10; ZLG7289_Download0(1,1,1,dat);//有小数点 result=result/10; dat=result%10; ZLG7289_Download0(1,2,0,dat);//下载数据 dat=0x0f; ZLG7289_Download0(0,4,0,dat);//译码方式0 dat=0x0f; ZLG7289_Download0(0,5,0,dat);//译码方式0 } /*************************************************** //电流显示 void ad0display (void) { dat=result0%10;// ZLG7289_Download(1,0,0,dat); dat=result0/1000; ZLG7289_Download(1,3,0,dat); result0=result0/10; dat=result0%10; ZLG7289_Download(1,1,0,dat); result0=result0/10; dat=result0%10; ZLG7289_Download(1,2,0,dat); dat=0; ZLG7289_Download(1,4,0,dat); dat=0; ZLG7289_Download(1,5,0,dat); } /*********************************************/ void cdisplay (void)//电容显示 { dat=resultc%10;// ZLG7289_Download(1,0,0,dat); resultc=resultc/10; dat=resultc%10; ZLG7289_Download(1,1,0,dat); resultc=resultc/10; dat=resultc%10; ZLG7289_Download(1,2,1,dat); resultc=resultc/10; dat=resultc%10; ZLG7289_Download(1,3,0,dat); resultc=resultc/10; dat=resultc%10; if (dat==0) dat=0x0f; ZLG7289_Download(0,4,0,dat); dat=resultc/10; if (dat==0)///当最高位为0时不显示 dat=0x0f; //else //dat=dat; ZLG7289_Download(0,5,0,dat); } void addisplay0 (void)//上电显示0 {uchar i; dp=0; dat=0; for (i=0;i《6;i++) { ZLG7289_Download0(1,i,dp,dat); } } void autoflashdisplay (void)自动闪烁显示 { uchar n; //用不译码的方式显示自己定义的数据 for ( n=0; n《6; n++ ) { dp = 0; dat = autodat[n];//;;;;;; x=5-n; { ZLG7289_Download(2,x,dp,dat); dat = 0x00;//闪烁控制,为1不闪烁 ZLG7289_Flash(dat); } } } void autounflashdisplay (void)//自动不闪烁显示 { uchar n; //用不译码的方式显示自己定义的数据 for ( n=0; n《6; n++ ) { dp = 0; dat = autodat[n];//;;;;;; x=5-n; { ZLG7289_Download(2,x,dp,dat); dat = 0xFF;//闪烁控制,为1不闪烁 ZLG7289_Flash(dat); } } } void handflashdisplay (void)//手动闪烁显示 { for ( d=0; d《6;d++ ) { dat=dangwei[m][d];// x=5-d; { if(x==2) dp=1; else dp=0; } ZLG7289_Download(2,x,dp,dat); dat = 0x00;//闪烁控制,为1不闪烁 ZLG7289_Flash(dat); } } void handunflashdisplay (void)//手动不闪烁显示 { for ( d=0; d《6;d++ ) { dat=dangwei[m][d];// x=5-d; { if(x==2) dp=1; else dp=0; } ZLG7289_Download(2,x,dp,dat); dat = 0xff;//闪烁控制,为1不闪烁 ZLG7289_Flash(dat); } } void savedisplay (void)//显示保存的上一次的档位,既用于第二次上电显示或电容2S测试完显示档位 { do{ temp=REG_A;} //读积存器A while(temp&0x80);//为1时间更新很快将要发生。既为1时一直读并判断 flag2=TIME_FLAG1; m=TIME_M; { if (DUQUE==0)//auto,如果是自动 autounflashdisplay(); else//手动 handunflashdisplay(); } } uint adc(void) { uint u=0; uchar i; adcs=1; _nop_(); adcs=0; for(i=0;i《10;i++) //read data { clk=0; u=(u《《1)|dout;//串口读数 clk=1; _nop_(); } delay_ad(2); //delay 25uS adcs=1; return(u); //return ad data } uint adc0(void) { uint u=0; uchar i; adcs0=1; _nop_(); adcs0=0; for(i=0;i《10;i++) //read data { clk=0; u=(u《《1)|dout;//串口读数 clk=1; _nop_(); } delay_ad(2); //delay 25uS adcs0=1; return(u); //return ad data } // 定义一个发送字节数据到串口的函数 void delay_ad(uchar us) //delay time {while(us--); } /******************************************************************* void dazhuanghuan(unsigned int da)//DA5615转换 现在已经不用了 { unsigned int i; da《《=6; CS_DA=0; SPI_CLK=0; for (i=0;i《12;i++) { SPI_DATA=(bit)(da&0x8000); SPI_CLK=1; da《《=1; SPI_CLK=0; } CS_DA=1; SPI_CLK=0; for (i=0;i《12;i++); } *******************************************************************/ void savec (void)//保存检测计算的电容值,保存到12887中 { { do{ temp=REG_A;} //读积存器A while(temp&0x80);//为1时间更新很快将要发生。既为1时一直读并判断 } { C_ROMLL=c.a.a4;//保存小数位,32位,现在只需要保存24位 C_ROML=c.a.a3; C_ROMH=c.a.a2; } } void savev (void)//保存检测计算的电电压值 { { do{ temp=REG_A;} //读积存器A while(temp&0x80);//为1时间更新很快将要发生。既为1时一直读并判断 } { temp=printv%10; printv1=temp;//0 temp=printv/1000; printv4=temp;//3 printv=printv/10; temp=printv%10; printv2=temp;//1 printv=printv/10; temp=printv%10; printv3=temp;//2 } } void readc (void)//读保存检测计算的电容值为打印 { { do{ temp=REG_A;} //读积存器A while(temp&0x80);//为1时间更新很快将要发生。既为1时一直读并判断 } { c.a.a4=C_ROMLL;//保存小数位 c.a.a3=C_ROML; c.a.a2=C_ROMH; c.a.a1=0; } } void printcap (void)//打印检测计算的电容值 { { do{ temp=REG_A;} //读积存器A while(temp&0x80);//为1时间更新很快将要发生。既为1时一直读并判断 } { prc=c.printc; c1=prc%10;//最底位 prc=prc/10; c2=prc%10; prc=prc/10; c3=prc%10; prc=prc/10; c4=prc%10; prc=prc/10; c5=prc%10; prc=prc/10; c6=prc%10; /*最高位为0就不打印0***********/ c6=c6|0x30; if (c6==0x30) c6=0; Print_Byte(c6); c5=c5|0x30; if (c5==0x30) c5=0; Print_Byte(c5); Print_Byte(c4|0x30); Print_Byte(c3|0x30); Print_Byte(0x2E);//打印小数点 Print_Byte(c2|0x30); Print_Byte(c1|0x30); Print_String(“微法”); } } void prv (void)//打印检测计算的电电压值 { { do{ temp=REG_A;} //读积存器A while(temp&0x80);//为1时间更新很快将要发生。既为1时一直读并判断 } { temp=printv4; Print_Byte(temp|0x30); temp=printv3; Print_Byte(temp|0x30); temp=printv2; Print_Byte(temp|0x30); Print_Byte(0x2E);//打印小数点 temp=printv1; Print_Byte(temp|0x30); } } /**************************************************************/ void Print_Byte(uchar ch) { /* while(BUSY) { } */ SBUF=ch; while(!TI)//f发送完毕TI=1, { } TI=0;//软件清出发送完毕标志位 } //定义一个把字符串送到串口的函数 void Print_String(uchar *str) { while(*str)//while(1) { Print_Byte(*str++); } } /*************************************************** //定义发送一个数组的数据到串口的函数 void Print_Array(array,n) uchar array[]; int n; { int i; for(i=0;i《n;i++) Print_Byte(array); } 2007.7.15 10:26 作者:wind 收藏 | 评论:0 电容测试程序2 分类:默认栏目/**************************************************/ /*外部中断1///已经不用了 void INT1_SVC() interrupt 2 using 1 { p1_4=1; EA=0; EX1=0;//关外部中断1, EA=1; TMOD=0X01;//00000001B;//定时器T0内部控制,定时方式,模式1。 TH0=0xB8; TL0=0x00;//定时10MS TR0=1;//;计算开始 } /**************************************************/ //定时器0中断///已经不用了 /*void timer0() interrupt 1 using 2 { TF0=0;//清除中断申请 TR0=0;//关定时器) p1_4=0; } /**************************************** |
|
|
|
只有小组成员才能发言,加入小组>>
4539个成员聚集在这个小组
加入小组3343 浏览 0 评论
航顺(HK)联合电子发烧友推出“近距离体验高性能Cortex-M3,免费申请价值288元评估板
4270 浏览 1 评论
4296 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-28 06:37 , Processed in 0.988938 second(s), Total 76, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号