fish001

  • 2020-12-12
  • 发表了主题帖: DSP 数据的定标

    一 DSP定点算数运算 1 数的定标 在定点DSP芯片中,采用定点数进行数值运算,其操作数一般采用整型数来表示。一个整型数的最大表示范围取决于DSP芯片所给定的字长,一般为16位或24位。显然,字长越长,所能表示的数的范围越大,精度也越高。如无特别说明,本书均以16位字长为例。 DSP芯片的数以2的补码形式表示。每个16位数用一个符号位来表示数的正负,0表示数值为正,l则表示数值为负。其余15位表示数值的大小。因此, 二进制数0010000000000011b=8195 二进制数1111111111111100b= -4 对DSP芯片而言,参与数值运算的数就是16位的整型数。但在许多情况下,数学运算过程中的数不一定都是整数。那么,DSP芯片是如何处理小数的呢?应该 说,DSP芯片本身无能为力。那么是不是说DSP芯片就不能处理各种小数呢?当然不是。这其中的关键就是由程序员来确定一个数的小数点处于16位中的哪一 位。这就是数的定标。 通过设定小数点在16位数中的不同位置,就可以表示不同大小和不同精度的小数了。数的定标有Q表示法和S表示法两种。表1.1列出了一个16位数的16种Q表示、S表示及它们所能表示的十进制数值范围。 从表1.1可以看出,同样一个16位数,若小数点设定的位置不同,它所表示的数也就不同。例如, 16进制数2000H=8192,用Q0表示 16进制数2000H=0.25,用Q15 但对于DSP芯片来说,处理方法是完全相同的。 从表1.1还可以看出,不同的Q所表示的数不仅范围不同,而且精度也不相同。Q越大,数值范围越小,但精度越高;相反,Q越小,数值范围越大,但精度就越 低。例如,Q0 的数值范围是一32768到+32767,其精度为1,而Q15的数值范围为-1到0.9999695,精度为1/32768=0.00003051。因 此,对定点数而言,数值范围与精度是一对矛盾,一个变量要想能够表示比较大的数值范围,必须以牺牲精度为代价;而想精度提高,则数的表示范围就相应地减 小。在实际的定点算法中,为了达到最佳的性能,必须充分考虑到这一点。 浮点数与定点数的转换关系可表示为: 浮点数(x)转换为定点数(xq):xq=(int)x* 2Q 定点数(xq)转换为浮点数(x):x=(float)xq*2-Q 例如,浮点数x=0.5,定标Q=15,则定点数xq=L0.5*32768J=16384,式中LJ表示下取整。反之,一个用Q=15表示的定点数 16384,其浮点数为163幼*2-15=16384/32768=0.5。浮点数转换为定点数时,为了降低截尾误差,在取整前可以先加上0.5。 表1.1 Q表示、S表示及数值范围 Q表示 S表示 十进制数表示范围 Q15 S0.15 -1≤x≤0.9999695 Q14 S1.14 -2≤x≤1.9999390 Q13 S2.13 -4≤x≤3.9998779 Q12 S3.12 -8≤x≤7.9997559 Q11 S4.11 -16≤x≤15.9995117 Q10 S5.10 -32≤x≤31.9990234 Q9 S6.9 -64≤x≤63.9980469 Q8 S7.8 -128≤x≤127.9960938 Q7 S8.7 -256≤x≤255.9921875 Q6 S9.6 -512≤x≤511.9804375 Q5 S10.5 -1024≤x≤1023.96875 Q4 S11.4 -2048≤x≤2047.9375 Q3 S12.3 -4096≤x≤4095.875 Q2 S13.2 -8192≤x≤8191.75 Q1 S14.1 -16384≤x≤16383.5 Q0 S15.0 -32768≤x≤32767 2 高级语言:从浮点到定点 我们在编写DSP模拟算法时,为了方便,一般都是采用高级语言(如C语言)来编写模拟程序。程序中所用的变量一般既有整型数,又有浮点数。如例1.1程序中的变量i是整型数,而pi是浮点数,hamwindow则是浮点数组。 例1.1 256点汉明窗计算 int i;+ float pi=3.14l59; float hamwindow[256]; for(i=0;i<256;i++) hamwindow=0.54-0.46*cos(2.0*pi*i/255); 如果我们要将上述程序用某种足点DSP芯片来实现,则需将上述程序改写为DSP芯片的汇编语言程序。为了DSP程序调试的方便及模拟定点DSP实现时的算 法性能,在编写DSP汇编程序之前一般需将高级语言浮点算法改写为高级语言定点算法。下面我们讨论基本算术运算的定点实现方法。 2.1 加法/减法运算的C语言定点摸拟 设浮点加法运算的表达式为: float x,y,z; z=x+y; 将浮点加法/减法转化为定点加法/减法时最重要的一点就是必须保证两个操作数的定标 temp=x+temp; z=temp>>(Qx-Qz),若Qx>=Qz z=temp<<(Qz-Qx),若Qx<=Qz 例1.4结果超过16位的定点加法 设x=l5000,y=20000,则浮点运算值为z=x+y=35000,显然z>32767,因此 Qx=1,Qy=0,Qz=0,则定点加法为: x=30000;y=20000; temp=20000<<1=40000; temp=temp+x=40000+30000=70000; z=70000L>>1=35000; 因为z的Q值为0,所以定点值z=35000就是浮点值,这里z是一个长整型数。当加法或加法的结果超过16位表示范围时,如果程序员事先能够了解到这种 情况,并且需要保持运算精度时,则必须保持32位结果。如果程序中是按照16位数进行运算的,则超过16位实际上就是出现了溢出。如果不采取适当的措施, 则数据溢出会导致运算精度的严重恶化。一般的定点DSP芯片都没有溢出保护功能,当溢出保护功能有效时,一旦出现溢出,则累加器ACC的结果为最大的饱和 值(上溢为7FFFH,下溢为8001H),从而达到防止溢出引起精度严重恶化的目的。 2.2乘法运算的C语言定点模拟 设浮点乘法运算的表达式为: float x,y,z; z=xy; 假设经过统计后x的定标值为Qx,y的定标值为Qy,乘积z的定标值为Qz,则 z=xy zq*2-Qx=xq*yq*2-(Qx+Qy) zq=(xqyq)2Qz-(Qx+Qy) 所以定点表示的乘法为: int x,y,z; long temp; temp=(long)x; z=(temp*y)>>(Qx+Qy-Qz); 例1.5定点乘法。 设x=18.4,y=36.8,则浮点运算值为=18.4*36.8=677.12; 根据上节,得Qx=10,Qy=9,Qz=5,所以 x=18841;y=18841; temp=18841L; z=(18841L*18841)>>(10+9-5)=354983281L>>14=21666; 因为z的定标值为5,故定点z=21666,即为浮点的z=21666/32=677.08。 2.3除法运算的C语言定点摸拟 设浮点除法运算的表达式为: float x,y,z; z=x/y; 假设经过统计后被除数x的定标值为Qx,除数y的定标值为Qy,商z的定标值为Qz,则 z=x/y zq*2-Qz=(xq*2-Qx)/(yq*2-Qy) zq=(xq*2(Qz-Qx+Qy))/yq 所以定点表示的除法为: int x,y,z; long temp; temp=(long)x; z=(temp<<(Qz-Qx+Qy))/y; 例1.6定点除法。 设x=18.4,y=36.8,浮点运算值为z=x/y=18.4/36.8=0.5; 根据上节,得Qx=10,Qy=9,Qz=15;所以有 z=18841,y=18841; temp=(long)18841; z=(18841L<<(15-10+9)/18841=3O8690944L/18841=16384; 因为商z的定标值为15,所以定点z=16384,即为浮点z=16384/215=0.5。 2.4程序变量的Q值确定 在前面几节介绍的例子中,由于x,y,z的值都是已知的,因此从浮点变为定点时Q值很好确定。在实际的DSP应用中,程序中参与运算的都是变量,那么如何 确定浮点程序中变量的Q值呢?从前面的分析可以知道,确定变量的Q值实际上就是确定变量的动态范围,动态范围确定了,则Q值也就确定了。 设变量的绝对值的最大值为|max|,注意|max|必须小于或等于32767。取一个整数n,使满足 2n-1<|max|<2n 则有 2-Q=2-15*2n=2-(15-n) Q=15-n 例如,某变量的值在-1至+1之间,即|max|<1,因此n=0,Q=15-n=15。 既然确定了变量的|max|就可以确定其Q值,那么变量的|max|又是如何确定的呢?一般来说,确定变量的|max|有两种方法。一种是理论分析法,另一种是统计分析法。 1. 理论分析法 有些变量的动态范围通过理论分析是可以确定的。例如: (1)三角函数。y=sin(x)或y=cos(x),由三角函数知识可知,|y|<=1。 (2)汉明窗。y(n)=0.54一0.46cos[nπn/(N-1)],0<=n<=N-1。因为-1<=cos[2πn/(N-1)]<=1,所以0.08<=y(n)<=1.0。 (3)FIR卷积。y(n)=∑h(k)x(n-k),设∑|h(k)|=1.0,且x(n)是模拟信号12位量化值,即有|x(n)|<=211,则|y(n)|<=211。 (4)理论已经证明,在自相关线性预测编码(LPC)的程序设计中,反射系数ki满足下列不等式:|ki|<1.0,i=1,2,...,p,p为LPC的阶数。 2. 统计分析法 对于理论上无法确定范围的变量,一般采用统计分析的方法来确定其动态范围。所谓统计分析,就是用足够多的输入信号样值来确定程序中变量的动态范围,这里输 入信号一方面要有一定的数量,另一方面必须尽可能地涉及各种情况。例如,在语音信号分析中,统计分析时就必须来集足够多的语音信号样值,并且在所采集的语 音样值中,应尽可能地包含各种情况。如音量的大小,声音的种类(男声、女声等)。只有这样,统计出来的结果才能具有典型性。 当然,统计分析毕竟不可能涉及所有可能发生的情况,因此,对统计得出的结果在程序设计时可采取一些保护措施,如适当牺牲一些精度,Q值取比统计值稍大些,使用DSP芯片提供的溢出保护功能等。 2.5浮点至定点变换的C程序举例 本节我们通过一个例子来说明C程序从浮点变换至定点的方法。这是一个对语音信号(0.3~3.4kHz)进行低通滤波的C语言程序,低通滤波的截止频率为 800Hz,滤波器采用19点的有限冲击响应FIR滤波。语音信号的采样频率为8kHz,每个语音样值按16位整型数存放在insp.dat文件中。 例1.7语音信号800Hz 19点FIR低通滤波C语言浮点程序。 #i nclude const int length=180/*语音帧长为180点=22.5ms@8kHz采样*/ void filter(int xin[],int xout[],int n,float h[]);/*滤波子程序说明*/ /*19点滤波器系数*/ static float h[19]= {0.01218354,-0.009012882,-0.02881839,-0.04743239,-0.04584568, -0.008692503,0.06446265,0.1544655,0.2289794,0.257883, 0.2289794,0.1544655,0.06446265,-0.008692503,-0.04584568, -0.04743239,-0.02881839,-0.009012882,O.01218354}; static int xl[length+20]; /*低通滤波浮点子程序*/ void filter(int xin[],int xout[],int n,float h[]) { int i,j; float sum; for(i=0;i for(i=0;i<length;i++) { sum=0.0; for(j=0;j<n;j++)sum+=h[j]*x1[i-j+n-1]; xout=(int)sum; for(i=0;i<(n-l);i++)x1[n-i-2]=xin[length-1-i]; } /*主程序*/ void main() FILE *fp1,*fp2; int frame,indata[length],outdata[length]; fp1=fopen(insp.dat,"rb");/* 输入语音文件*/ fp2=fopen(Outsp.dat,"wb");/* 滤波后语音文件*/ frame=0; while(feof(fp1) ==0) { frame++; printf(“frame=%d\n”,frame); for(i=0;i<length;i++)indata=getw(fp1); /*取一帧语音数据*/ filter(indata,outdata,19,h);/*调用低通滤波子程序*/ for(i=0;i<length;i++)putw(outdata,fp2);/*将滤波后的样值写入文件*/ } fcloseall();/*关闭文件*/ return(0); } 例1.8语音信号800Hz l9点FIR低通滤波C语言定点程序。 #i nclude const int length=180; void filter (int xin[],int xout[],int n,int h[]); static int h[19]={399,-296,-945,-1555,-1503,-285,2112,5061,7503,8450, 7503,5061,2112,-285,-1503,-1555,-945,-296,399};/*Q15*/ static int x1[length+20]; /*低通滤波定点子程序*/ void filter(int xin[],int xout[],int n,int h[]) int i,j; long sum; for(i=0;i<length;i++)x1[n+i-111=xin]; for(i=0;i<1ength;i++) sum=0; for(j=0;j<n;j++)sum+=(long)h[j]*x1[i-j+n-1]; xout=sum>>15; for(i=0;i<(n-1);i++)x1[n-i-2]=xin[length-i-1]; } 主程序与浮点的完全一样。“ 3 DSP定点算术运算 定点DSP芯片的数值表示基于2的补码表示形式。每个16位数用l个符号位、i个整数位和15-i个小数位来表示。因此: 00000010.10100000 表示的值为: 21+2-1+2-3=2.625 这个数可用Q8格式(8个小数位)来表示,其表示的数值范围为-128至+l27.996,一个Q8定点数的小数精度为1/256=0.004。 虽然特殊情况(如动态范围和精度要求)必须使用混合表示法。但是,更通常的是全部以Q15格式表示的小数或以Q0格式表示的整数来工作。这一点对于主要是 乘法和累加的信号处理算法特别现实,小数乘以小数得小数,整数乘以整数得整数。当然,乘积累加时可能会出现溢出现象,在这种情况下,程序员应当了解数学里 面的物理过程以注意可能的溢出情况。下面我们来讨论乘法、加法和除法的DSP定点运算,汇编程序以TMS320C25为例。 3.1定点乘法 两个定点数相乘时可以分为下列三种情况: 1. 小数乘小数 例1.9 Q15*Q15=Q30 0.5*0.5=0.25 0.100000000000000;Q15 * 0.100000000000000;Q15 -------------------------------------------- 00.010000000000000000000000000000=0.25;Q30 两个Q15的小数相乘后得到一个Q30的小数,即有两个符号位。一般情况下相乘后得到的满精度数不必全部保留,而只需保留16位单精度数。由于相乘后得到的高16位不满15位的小数据度,为了达到15位精度,可将乘积左移一位,下面是上述乘法的TMS320C25程序: LT OP1;OP1=4000H(0.5/Q15) MPY OP2;oP2=4000H(0.5/Ql5) PAC SACH ANS,1;ANS=2000H(0.25/Q15)

  • 发表了主题帖: DSP C54X ioport的用法

    DSP中如何访问I/O,数据空间 访问I/O空间 I/O空间地址声明 要在程序中访问io空间地址,必须首先用关键字“ioport”对要访问的地址进行定义。 语法:ioport t ype porthex_num ioport 声明io空间端口变量的关键字; type 变量类型,可以为char, short, int或unsigned int; porthex_num 端口号,port后面接16进制数字。 ioport unsigned int port10; 注:声明io空间地址必须在C文件起始声明,不允许在函数中使用ioport声明io空间地址。 I/O空间地址访问 访问用ioport关键字声明的I/O端口变量和访问一般变量没有区别。 ioport unsigned int port10; int func () { ... port10 = a; ... b = port10; ... } I/O端口变量的使用不仅仅局限于赋值,和其他变量同样也可以应用于其它的表达式。 call (port10); a = port10 + b; port10 += a; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 程序中访问的任何一个IO地址都必须在C语言程序起始处用ioport关键字声明! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 访问数据空间 访问数据空间不需要对要访问的单元预先声明,访问是通过指针的方法实现的。 unsigned int org,cnt,block,offset,tmp,i; org = *(unsigned int *) 0x8000; cnt = *(unsigned int *) 0x8001; block = *(unsigned int *) 0x8002; offset = *(unsigned int *) 0x8003; for (i=0; i<cnt; i++) { tmp = *(unsigned int *) (org + i); *(unsigned int *) (org + offset +i) = tmp; }

  • 发表了主题帖: 利用单片机DAC和C语言产生正弦波数据

    以下为适用于各类单片机:   改变单片机的DAC输出电压,可以得到需要的电压波形输出,下面介绍正弦波所需的DAC数据是如何计算的。   首先既然是正弦波,那么就要确定输出一个周期正弦波的采样点数point,即由多少点组成了一周期的正弦波,还要知道单片机输出DAC的数字值maxnum是多少,比如8位DAC,maxnum=256。10位DAC,maxnum=1024。   知道以上两个值后,就开始计算需要得到的正弦波DA数据了,我设置一个正弦波由61个点组成,所选DA最大数字输入值为1024,那么我的正弦波数据数组就有61个数据,即sin_tab[61],也就是把一个正弦波360度,分成了61份,那么每份就是360÷61=5.901度,这样就可以计算出61点中每个点对应的角度值angle,有了角度值就可以算出来角度对应的正弦值,利用正弦值和输出DA的数字值maxnum,就可以计算出对应DA输入的数值了。 公式为:  sin_tab[i]=(maxnum/2) * sin(x)+(maxnum/2); //  i代表某点      x为某角度对应的弧度    弧度=角度*(π/180);  //(maxnum/2)为正弦波零点处对应DA输入值;即DA满量程的一半; 1 2 下面为代码: #include<math.h> //注意需添加此头文件,包含了求正弦值函数sin(弧度值); //获取不同点数的正弦波数据 //point: 一周期内的取样点数 //maxnum: 一周期内对应DA输出最大值 void getSinTab(uchar point,uint maxnum) { uchar i=0; float x;   //弧度 float angle;//角度 分度角 angle=360.000/point;   for(i=0;i<point;i++)   {       x=angle*i;    //得到角度值       x=x*0.01744; //角度转弧度  弧度=角度*(π/180)       sin_tab[i]=(maxnum/2)*sin(x)+(maxnum/2);   } } // sin_tab[i] 为得到的正弦波数据,用于送给DA输出。  

  • 发表了主题帖: 单片机 利用C语言产生正弦波DA数据

    通过改变单片机的DA输出电压,可以得到各种各样的电压波形输出,下面介绍产生正弦波形需送DA的数据是如何计算的。        首先既然是正弦波,那么就要确定要输出一个周期正弦波的采样点数point,即由多少点组成了一周期的正弦波,还要知道单片机输出DA的数字值maxnum是多少,比如 8位DA,maxnum=256。10位DA,maxnum=1024。        知道以上两个值后,就开始计算需要得到的正弦波DA数据了,我设置一个正弦波由61个点组成,所选DA最大数字输入值为1024,那么我的正弦波数据数组就有61个数据,即sin_tab[61],也就是把一个正弦波360度,分成了61份,那么每份就是360÷61=5.901度,这样就可以计算出61点中每个点对应的角度值jiaodu,有了角度值就可以算出来角度对应的正弦值,利用正弦值和输出DA的数字值maxnum,就可以计算出对应DA输入的数值了。 公式为:  sin_tab[i]=(maxnum/2)*sin(x)+(maxnum/2); //  i代表某点      x为某角度对应的弧度      弧度=角度*(π/180);  //(maxnum/2)为正弦波零点处对应DA输入值;即DA满量程的一半; 在51单片机运行了以下函数,DA输出波形完美,验证产生的DA数据无误。  #include<math.h> //注意需添加此头文件,包含了求正弦值函数sin(弧度值); //获取不同点数的正弦波数据 //point: 一周期内的取样点数 //maxnum: 一周期内对应DA输出最大值 void getSinTab(uchar point,uint maxnum) { uchar i=0; float x;   //弧度 float jiao;//角度 分度角 jiao=360.000/point;   for(i=0;i<point;i++) { x=jiao*i;    //得到角度值    x=x*0.01744; //角度转弧度  弧度=角度*(π/180)       sin_tab[i]=(maxnum/2)*sin(x)+(maxnum/2); } } // sin_tab[i] 为得到的正弦波数据,用于送给DA输出。  

  • 发表了主题帖: DSP2000&nbsp;5000&nbsp;6000系列

            应用领域:2000系列主要偏重于工控领域,这个系列的DSP自身集成了丰富的I/O口,A/D采样接口及PWM输出接口,多为定点型芯片;5000和6000系列主要偏重于视频图像处理,基本都为浮点型的,性能强大,但自身集成的外设很少,一般需要扩展(如6713+FPGA形式)。 1. C2000系列:         C2000系列是一个控制器系列,全部为16位定点DSP。该系列中的一些型号具有片内FLASH RAM,如TMS320F24x,TMS320LF240x等。TI所有DSP中,也只有C2000有FLASH。作为控制器,C2000系列除了有一个 DSP核以外,还有大量的外设资源,如A/D、定时器、各种串口(同步或异步)、WATCHDOG、CAN总线、PWM发生器、数字IO脚等等。特别是 C2000的异步串口可以与PC的UART相连,也是TI所有DSP中唯一具有异步串口的系列。 2. C5000系列:         C5000系列是一个定点低功耗系列,特别适用于手持通讯产品,如手机、PDA、GPS等。目前的处理速度一般在80MIPS一400MIPS。 C5000系列主要分为C54xx和C55xx两个系列。两个系列在执行代码级是兼容的,但他们的汇编指令系统却不同。目前TMS320VC5402的零售价在¥60 — ¥80元,性价比极高。C5000包含的主要外设有McBPS同步串口,HPI并行接口,定时器,DMA等。其中C55XX提供EMIF外部存储器扩展接口,允许用户直接使用SDRAM、SBSRAM、SRAM、EPROM等各种存储器。而C54XX没有提供EMIF,所以只能直接使用静态存储器SRAM和EPROM。另外,C5000系列一般都使用双电源供电,其I/0电压和核电压一般不同,而且不同型号也有差别。不过,TI提供了全系列的DC—DC变换器可以解决DSP的电源问题。 3. C6000系列:         C6000系列是一个32位的高性能的DSP芯片,目前处理速度从800MIPS一2400MIPS,而且还在不断提高。其中,C62XX为定点系列,C67XX和C64XX为浮点系列。同C55xx一样,C6000也提供EMIF扩展存储器接口,方便用户使用各种外部扩展存储器,如SBSRAM、 SDRAM、SRAM、EPROM。C6000提供的主要外设有McBPS同步串口,HPI并行接口,定时器,DMA等。另外,在C6000的一些型号中还提供了PCI接口。C6000几乎都只提供BGA球形封装,在PCB板制作时需要多层板,增加了开发和调试的难度。另外,C6000系列的功耗较大,需要仔细考虑DSP与系统其他部分的电力分配,选择适当的DC—DC转换器

  • 2020-12-11
  • 发表了主题帖: 串行通信接口的原理图

      初始化串口的Tx和Rx引脚所对应的GPIO

  • 发表了主题帖: 串口通信与中断的关系

     串口通信分为发送/接收两部分,发送一般不需要中断即可完成发送,接收一般需要使用中断来接收。             发送方可以选择使用中断,也可以选择不使用中断。使用中断的工作情景是:发送方先设置好中断并绑定一个中断处理程序,然后发送方丢一帧数据给transmitter,transmitter耗费一段时间来发送这一帧数据,这段时间内发送方CPU可以去做别的事情,等transmitter发送完成后会产生一个TXD中断,该中断会导致事先绑定的中断处理程序执行,在中断处理程序中CPU会切换回来继续给transmitter放一帧数据,然后CPU切换离开;不使用中断的工作情景是:发送方事先禁止TXD中断(当然也不需要给相应的中断处理程序了),发送方CPU给一帧数据到transmitter,然后transmitter耗费一段时间来发送这帧数据,这段时间CPU在这等着(CPU没有切换去做别的事情),待发送方发送完成后CPU再给它一帧数据继续发送直到所有数据发完。CPU是怎么知道transmitter已经发送完了?原来是有个状态寄存器,状态寄存器中有一个位叫发送缓冲区空标志,transmitter发送完成(发送缓冲区空了)就会给这个标志位置1,CPU就是通过不断查询这个标志位为1还是0来知道发送是否已经完成的。             接收方可以选择使用中断,也可以选择不使用中断。使用中断的工作情景是:接收方先设置好中断并绑定一个中断处理程序,然后接收方会耗费一段时间从receiver中来接收一帧数据,这段时间内接收方CPU可以去做别的事情,等receiver接收数据完成后会产生一个RXD中断,该中断会导致事先绑定的中断处理程序执行,在中断处理程序中CPU会切换回来从receiver中读取数据,然后CPU切换离开;不使用中断的工作情景是:接收方事先禁止RXD中断(当然也不需要给相应的中断处理程序了),接收方会耗费一段时间从receiver中接收一帧数据,这段时间CPU在这等着(CPU没有切换去做别的事情),待接收方接收完成后CPU会继续等着直到所有数据接收完。CPU是怎么知道receiver已经接收完了?原来是有个状态寄存器,状态寄存器中有一个位叫接收缓冲区满标志,receiver接收完成(接收缓冲区满了)就会给这个标志位置1,CPU就是通过不断查询这个标志位为1还是0来知道接收是否已经完成的。             因为串口通信是异步的,异步的意思就是说发送方占主导权。也就是说发送方随时想发就能发,但是接收方只有时刻等待才不会丢失数据。所以这个差异就导致发送方可以不用中断,而接收方不得不使用中断模式。     串行通信接口的时钟设计             串口通信为什么需要时钟?因为串口通信需要一个固定的波特率,所以transmitter和receiver都需要一个时钟信号。             时钟信号从哪里来?源时钟信号是外部APB总线(PCLK_PSYS,66MHz)提供给串口模块的(这就是为什么我们说串口是挂在APB总线上的),然后进到串口控制器内部后给波特率发生器(实质上是一个分频器),在波特率发生器中进行分频,分频后得到一个低频时钟,这个时钟就是给transmitter和receiver使用的。            串口通信中时钟的设置主要看寄存器设置。重点的有:寄存器源设置(为串口控制器选择源时钟,一般选择为PCLK_PSYS,也可以是SCLK_UART),还有波特率发生器的2个寄存器。            波特率发生器有2个重要寄存器:UBRDIVn和UDIVSLOTn,其中UBRDIVn是主要的设置波特率的寄存器,UDIVSLOTn是用来辅助设置的,目的是为了校准波特率的。

  • 发表了主题帖: 串口控制器工作原理框图

       整个串口控制器包含transmitter和receiver两部分,这两部分功能彼此独立,transmitter负责210向外部发送信息,receiver负责从外部接收信息到210。             总线角度来讲,串口控制器是挂载在APB总线上的,对编程有影响的是:将来计算串口控制器的源时钟时是以APB总线来计算的。             transmitter由发送缓冲区和发送移位寄存器构成。我们要发送信息时,首先将信息进行编码(一般用ASCII码)成二进制流,然后将一帧数据(一般是8位)写入发送缓冲区(从这里以后程序就不用管了,剩下的发送部分由硬件自动完成),最后发送移位寄存器会自动从发送缓冲区中读取一帧数据,然后自动移位(移位的目的是将一帧数据的各个位分别拿出来)将其发送到Tx通信线上。             receiver由接收缓冲区和接收移位寄存器构成。当有人通过串口线向我发送信息时,信息通过Rx通信线进入我的接收移位寄存器中,然后接收移位寄存器自动移位并将该二进制位保存入我的接收缓冲区,接收完一帧数据后receiver会产生一个中断给CPU,CPU收到中断后即可知道receiver接收满了一帧数据,就会来读取这帧数据。             总结:                     发送缓冲区和接收缓冲区是关键,发送移位寄存器和接收移位寄存器的工作都是自动的,不用编程控制的,所以我们写串口代码就是:首先初始化串口控制器(包括发送控制器和接收控制器),然后要发送信息时直接写入发送缓冲区,要接收信息时直接去接收缓冲区中读取即可。                     软件工程师对串口操作的接口就是发送/接收缓冲区(实际就是寄存器,操作方式就是读写内存)。             串口控制器中有一个波特率发生器,其作用是产生串口发送/接收的时钟节拍。波特率发生器其实就是个时钟分频器,它的工作需要源时钟(从APB总线来),然后内部将源时钟进行分频(需要软件设置寄存器来配置)得到目标时钟,然后再用这个目标时钟产生波特率(由硬件自动完成的)。    FIFO模式及其作用             典型的串口设计,发送/接收缓冲区只有1字节,每次发送/接收只能处理1帧数据。这样在单片机中没什么问题,但是到复杂SOC中(一般有操作系统的)就会有问题,会导致效率低下,因为CPU需要不断切换上下文。             解决方案就是想办法扩展串口控制器的发送/接收缓冲区,譬如将发送/接收缓冲区设置为64字节,CPU一次过来直接给发送缓冲区64字节的待发送数据,然后transmitter慢慢发送,发送完再找CPU再要64字节数据。但是串口控制器本来的发送/接收缓冲区是固定的1字节大小的,所有做了个变相的扩展,就是FIFO。CPU先将64字节的数据放到FIFO中,然后启动FIFO模式,FIFO每次会自动往发送缓冲区中添加1字节数据,最后进行移位操作传输数据。             FIFO,就是first in first out,先进先出。FIFO其实是一种数据结构,这里这个大的缓冲区叫FIFO是因为这个缓冲区的工作方式类似于FIFO这种数据结构。

  • 发表了主题帖: 串口通信详解

    一、通信的基础概念     (1)通信的发展历史             通信:狼烟 ---> 信件 ---> 电子通信(电报、电话、网络信号)。             通信中最重要的两个方面:信息表示、解析方法 + 信息的传输方法。             通信双方事先需要约定好信息的表示方法和解析方法,做到一致,否则信息不能有效传递。             通信的传输方法是指经过编码后的通信信息如果在传输介质上传输的过程。             总结:                     通信过程分为3个步骤:首先,发送方按照信息编码方式对有效信息进行编码(编成可以在通信线路上传输的信号形态);然后,编码后的信息在传输介质上进行传输,输送给接收方;最后,接收方接到编码信息后进行解码,解码后得到可以理解的有效信息。     (2)电子通信概念             1》同步通信和异步通信                   发送方和接收方按照同一个时钟节拍工作就叫同步。发送方和接收方没有统一的时钟节拍、而各自按照自己的节拍工作就叫异步。                   同步通信中,通信双方按照统一节拍工作,所以配合很好;一般需要发送方给接收方发送信息同时发送时钟信号,接收方根据发送方给它的时钟信号来安排自己的节奏。同步通信用在通信双方信息交换频率固定,或者经常通信时。                   异步通信,又叫异步通知。在双方通信的频率不固定时(有时3ms收发一次,有时3天才收发一次),不适合使用同步通信,而适合异步通信。异步通信时接收方不必一直在意发送方,发送方需要发送信息时会首先给接收方一个信息开始的起始信号,接收方接收到起始信号后就认为后面紧跟着的就是有效信息,才会开始注意接收信息,直到收到发送方发过来的结束标志。             2》电平信号和差分信号                   电平信号和差分信号是用来描述通信线路传输方式的。也就是说如何在通信线路上表示1和0。                   电平信号的传输线中有一个参考电平线(一般是GND),然后信号线上的信号值是由信号线电平和参考电平线的电压差决定。                   差分信号的传输线中没有参考电平线,所有都是信号线,然后1和0的表达靠信号线之间的电压差。                   电平信号的2根通信线之间的电平差异容易受到干扰,传输容易失败;差分信号不容易受到干扰,因此传输质量比较稳定。现代通信一般都使用差分信号,电平信号几乎没有了。                   在相同根数的通信线下,差分信号比电平信号要快,因为差分信号抗干扰能力强,因此1个发送周期更短。             3》并行接口和串行接口                   串行、并行主要是考虑通信线的根数,就是发送方和接收方同时可以传递的信息量的多少。                   在电平信号下,”1根参考电平线+1根信号线“可以传递1位二进制;”1根参考电平线+2根信号线“可以同时发送2位二进制;如果想同时发送8位二进制就需要9根线。                   在差分信号下,2根线(彼此差分)可以同时发送1位二进制;如果需要同时发送8位二进制就需要16根线。                   串行接口使用的更加广泛,因为更省信号线,而且对传输线的要求更低、成本更低;而且串行时可以通过提高通信速度来提高总体通信性能,不一定非得要并行。             4》总结                   经过这么多年的发展,最终胜出的是:异步、串行、差分,譬如USB和网络通信。 二、串口通信的基本概念     (1)串口通信特点             1》异步                   串口通信的发送方和接收方之间没有统一的时钟信号。             2》电平信号                  串口通信出现的时间较早、速率较低、传输的距离较近,所有干扰不太明显,因此当时使用了电平信号传输。后期出现的传输协议都改成了差分信号传输了。             3》串行通信                  串口通信每次同时只能传输1个二进制位。     (2)RS232电平和TTL电平             电平信号是用信号线电平减去参考线电平得到的电压差,这个电压差决定了传输值是1还是0。             在电平信号中多少V代表1,多少V代表0是不固定的,取决于电平标准。譬如,RS232电平中-15V ~ -3V表示1,+3V ~ +15V表示0;TTL电平中+5V表示1,0V表示0。             不管哪种电平都是为了在传输线上表示1和0,区别在于适用的环境和条件不同。RS232电平适合干扰大、距离远的情况,一般传输距离小于15米,用于工业上;TTL电平适合距离近且干扰小的情况,一般用在电路板内部的两个芯片之间。             对于编程来说,RS232电平传输和TTL电平传输是没有差异的。所以电平标准对硬件工程更有意义,而软件工程师只要略懂即可(TTL电平和RS232电平混接是不可以的)。     (3)波特率             波特率,指的是串口通信的速率,也就是串口通信时每秒钟可以传输多少个二进制位。譬如,每秒钟可以传输9600个二进制位(传输一个二进制位需要的时间是1/9600秒,也就是104us),波特率就是9600。             串口通信的波特率不能随意设定,而应该是在一些值中去选择。一般最常见的波特率是9600或115200(低端单片机如51常用9600,高端单片机和嵌入式SOC常用115200)。             波特率不能随意指定,主要是因为:通信双方必须事先设定相同的波特率才能成功通信,如果发送方和接收方按照不同的波特率通信则根本收不到,因此波特率最好是大家熟知的而不是随意指定的;常用的波特率经过长久发展,就形成了共识,大家常用的就是9600或者115200。     (4)起始位、数据位、奇偶校验位、停止位             串口通信时,收发是一个周期一个周期进行的,每个周期传输n个二进制位。这一个周期就叫做一个通信单元,一个通信单元由:起始位+数据位+奇偶校验位+停止位组成的。             起始位:表示发送方要开始发送一个通信单元,起始位的定义是串口通信标准事先指定的,是由通信线上的电平变化来反映的。             数据位:是一个通信单元中发送的有效信息位,是本次通信真正要发送的有效数据,串口通信一次发送多少位有效数据是可以设定的(可选的有6、7、8、9,一般都是选择8位数据位,因为一般通过串口发送的文字信息都是ASCII码编码,而ASCII码中一个字符刚好编码为8位)。             校验位:是用来校验数据位,以防止数据位出错的。             停止位:是发送方用来表示本通信单元结束标志的,停止位的定义是串口通信标准事先指定的,是由通信线上的电平变化来反映的。常见的有1位停止位、1.5位停止位、2位停止位等,一般使用的是1位停止位。             总结:                     串口通信时因为是异步通信,所以通信双方必须事先约定好通信参数,这些通信参数包括:波特率、数据位、校验位、停止位(串口通信中起始位定义是唯一的,所以一般不用选择)。     (5)单工、半双工和全双工             单工:单方向收发数据,譬如,只能A发送数据,B接收数据。             半双工:双方分时收发数据,譬如,“A发送数据,B接收数据”或者“A接收数据,B发送数据”,两个方向不能同时进行。             全双工:双方同时收发数据,譬如,“A发送数据,B接收数据”同时“A接收数据,B发送数据”,两个方向同时进行。 三、串口通信的基本原理     (1)三根通信线:TX、RX、GND             任何通信都要有信息传输载体,或者是有线的或者是无线的。             串口通信是有线通信,是通过串口线来通信的。             串口线最少需要两根(GND和信号线),可以实现单工通信;也可以使用3根通信线(TX、RX、GND),来实现全双工通信。             一般开发板都会引出SOC上串口引脚直接输出的TTL电平的接口,用的是插针式插座,每个串口引出的都有3根通信线(TX、RX、GND),可以用这些插座直接连接外部的TTL电平的串口设备。     (2)收发双方事先规定好通信参数(波特率、数据位、奇偶校验位、停止位等)             串口通信属于基层基本性的通信规约,它自己本身不会去协商通信参数,需要通信前通信双方事先约定好通信参数(一般4个最重要的)             串口通信的任何一个关键参数设置错误,都会导致通信失败。譬如波特率调错了,发送方发送没问题,接收方也能接收,但是接收到全是乱码···     (3)信息以二进制流的方式在信道上传输             串口通信的发送方每隔一定时间(时间固定为1/波特率,单位是秒)将有效信息(1或者0)放到通信线上去,逐个二进制位的进行发送。             接收方通过定时(起始时间由读到起始位标志开始,间隔时间由波特率决定)读取通信线上的电平高低来区分发送给我的是1还是0。依次读取数据位、奇偶校验位、停止位,停止位就表示这一个通信单元(帧)结束,然后中间是不定长短的非通信时间(发送方有可能紧接着就发送第二帧,也可能半天都不发第二帧,这就叫异步通信),下来就是第二帧·····             总结:波特率非常重要,波特率错了整个通信就乱套了;数据位、奇偶校验位、停止位也很重要,否则可能认不清数据。通过串口不管发数字、还是文本还是命令还是什么,都要先对发送内容进行编码,编码成二进制再进行逐个位的发送。             串口发送的一般都是字符,一般都是ASCII码编码后的字符,所以一般设置数据位都是8,方便刚好一帧发送1个字符。     (4)DB9接口介绍             DB9接口是串口通信早期比较常用的一种规范化接口。             串行通信在早期是计算机与外界通信的主要手段,那时候的计算机都有标准配置的串口以实现和外部通信。那时候就定义了一套标准的串口规约,DB9接口就是标准接口。             DB9接口中有9根通信线,其中3根很重要,为GND、Tx、Rx,必不可少;剩余6根都是和流控有关的,现代我们使用串口都是用来做调试一般都禁用流控,所以这6根没用。             现在一般使用串口时要记得把流控禁止掉,不然可能发生意想不到的问题。 四、S5PV210串行通信接口详解     (1)串口的名称             S5PV210的串口控制器在数据手册的section 8.1章节。             串口的官方名称是:UNIVERSAL ASYNCHRONOUS RECEIVER AND TRANSMITTER,即通用异步收发器,英文缩写是uart,中文简称串口。

  • 发表了主题帖: DSP28335使用FIFO的串口中断总结

    实现一个功能的时候首先查看相关的资料,例如数据手册,论坛、百度、书籍等,搜集各种相关资料,然后看别人是如何实现的,分析下相关步骤,理清好思路。针对不懂的地方继续查找资料,层层递进。(如果想省事,可以在别人正确代码的基础上进行修改,看他配置了什么寄存器,实现了什么功能,然后根据自己的需求,查看数据手册重新配置),实现的时候可以一个个小功能的实现,遇到疑惑的除求助外,也可试着观察不同的情况下会出现什么结果。总之就是多搜,多想,多动手,多总结。 #include "DSP2833x_Device.h" #include "DSP2833x_Examples.h" #define SCIB 0 #define SCIC 1 // Prototype statements for functions found within this file. void scic_fifo_init(void); void scic_xmit(int a); interrupt void uart_send(void);   // Global counts used in this example Uint16 isrCount=0; Uint16 ErrorCount=0; Uint16 sdata[4]={5,5,5,5};//要发送的数据 void main(void) {     Uint16 i=0; // Step 1. Initialize System Control registers, PLL, WatchDog, Clocks to default state: // This function is found in the DSP2833x_SysCtrl.c file.     InitSysCtrl();   // Step 2. Select GPIO for the device or for the specific application: // This function is found in the DSP2833x_Gpio.c file. // InitGpio(); skip this as this is example selects the I/O // for SCI-A in this file itself    InitSciGpio();   // Step 3. Initialize PIE vector table: // The PIE vector table is initialized with pointers to shell Interrupt // Service Routines (ISR).  The shell routines are found in DSP2833x_DefaultIsr.c. // Insert user specific ISR code in the appropriate shell ISR routine in // the DSP28_DefaultIsr.c file.   // Disable and clear all CPU interrupts:     DINT;     IER = 0x0000;     IFR = 0x0000;         // Initialize Pie Control Registers To Default State:       // This function is found in the DSP2833x_PieCtrl.c file.       // InitPieCtrl();  PIE is not used for this example         // Initialize the PIE Vector Table To a Known State:       // This function is found in DSP2833x_PieVect.c.       // This function populates the PIE vector table with pointers       // to the shell ISR functions found in DSP2833x_DefaultIsr.c.       InitPieVectTable();         // Enable CPU and PIE interrupts       // This example function is found in the DSP2833x_PieCtrl.c file.       EnableInterrupts();   // Step 4. Initialize all the Device Peripherals to a known state: // This function is found in DSP2833x_InitPeripherals.c // InitPeripherals(); skip this for SCI tests // Step 5. User specific functions, Reassign vectors (optional), Enable Interrupts:       isrCount = 0;     ErrorCount = 0; #if SCIB     scib_fifo_init();       // Initialize the SCI FIFO     scib_loopback_init();  // Initalize SCI #elif SCIC     scic_fifo_init();       // Initialize the SCI FIFO #endif // Send a character ,先给发送缓冲寄存器赋值。     for(i=0;i<4;i++)     {         scic_xmit(sdata[i]+0x30);     }    scic_xmit(' '); // Step 6. Send Characters forever starting with 0x00 and going through // 0xFF.  After sending each, check the recieve buffer for the correct value  EALLOW;   //  PieVectTable.SCIRXINTC = &uartIsr;       PieVectTable.SCITXINTC = &uart_send;       EDIS;       PieCtrlRegs.PIECTRL.bit.ENPIE=1;   //  PieCtrlRegs.PIEIER8.bit.INTx5=1;       PieCtrlRegs.PIEIER8.bit.INTx6=1;       IER|=M_INT8;       EINT;       ERTM;         for(;;){ } } interrupt void uart_send(void)   {       Uint16 i;        isrCount++;     sdata[3]=isrCount;     for(i=0;i<4;i++)     {       scic_xmit(sdata[i]+0x30);     }       scic_xmit(' ');     if(isrCount==10)     {         ScicRegs.SCICTL1.bit.TXENA =0;//禁止发送缓冲器工作。     }     PieCtrlRegs.PIEACK.all=0xffff;//0x0080;     ScicRegs.SCIFFTX.bit.TXFFINTCLR=1;    // Clear SCI Interrupt flag }     // Transmit a character from the SCI' void scic_xmit(int a)//发送一个数据a,类型为int {    ScicRegs.SCITXBUF=a;//向数据缓冲寄存器中写入数据即可发送该数据 }   // Initalize the SCI FIFO void scic_fifo_init() { // Test 1,SCIC  DLB, 8-bit word, baud rate 9.6k, default, 1 STOP bit, no parity //功能是配置发送模式 // Note: Clocks were turned on to the SCIC peripheral //  in the InitSysCtrl() function //  ScicRegs.SCICTL1.all =0x0000; //开始的时候先禁止接收与发送功能     ScicRegs.SCICCR.all =0x0007;   // 1 stop bit,  No loopback                                    // No parity,8 char bits,                                                       // async mode, idle-line protocol //数据长度8位,一个结束位,无奇偶校验,空闲线模式,禁止回送                                       //  ScicRegs.SCICTL1.all =0x0003;  // enable TX, RX, internal SCICLK,                                       // Disable RX ERR, SLEEP, TXWAKE                                         ScicRegs.SCICTL1.all =0x0002; //允许发送,禁止接收                               //  ScicRegs.SCICTL2.all =0x0001;//发送缓冲器中断使能。似乎与下面的重复了     ScicRegs.SCICTL2.bit.TXINTENA =1;//发送缓冲器中断使能。 //  ScicRegs.SCICTL2.bit.RXBKINTENA =1;     ScicRegs.SCIHBAUD    =0x0001;     ScicRegs.SCILBAUD    =0x00e7;  //上面是波特率设置,书上写的0x00e7         //  ScicRegs.SCICCR.bit.LOOPBKENA =0;// enable(Disable) loop back     ScicRegs.SCICTL1.all =0x0022;     // Relinquish SCI from Reset //FIFO设置     ScicRegs.SCIFFTX.bit.TXFIFOXRESET=0; //  ScicRegs.SCIFFRX.bit.RXFIFORESET=0;       ScicRegs.SCIFFTX.all=0xE060; //  ScicRegs.SCIFFRX.all=0x204f;     ScicRegs.SCIFFCT.all=0x0; }   //=========================================================================== // No more. //===========================================================================    

  • 发表了主题帖: DSP_28335_SCI_FIFO收发实验

    利用串口助手 SCIA口自收自发,串口助手发送12个字符,会触发一次接收中断,在中断服务函数中,将数据发回串口助手,自己改写的程序,亲测好用 图为实验结果截图 下为实验代码 /**************************************************************  * SCIA口自收自发,串口助手发送12个字符,会触发一次接收中断,在中断服务函数中,将数据发回串口助手 **************************************************************/ #include "DSP2833x_Device.h"     // DSP2833x Headerfile Include File #include "DSP2833x_Examples.h"   // DSP2833x Examples Include File #include "string.h" // Prototype statements for functions found within this file. interrupt void sciaTxFifoIsr(void); interrupt void sciaRxFifoIsr(void); void scia_fifo_init(void); void error(void); void scia_xmit(int a); void scia_msg(char *msg); char m[12]; char *msg;//定义指针 void main(void) {    InitSysCtrl();    InitSciGpio();    DINT;    InitPieCtrl();    IER = 0x0000;    IFR = 0x0000;    InitPieVectTable();    EALLOW;        // This is needed to write to EALLOW protected registers    PieVectTable.SCIRXINTA = &sciaRxFifoIsr;    PieVectTable.SCITXINTA = &sciaTxFifoIsr;    EDIS;   // This is needed to disable write to EALLOW protected registers    scia_fifo_init();  // Init SCI-A    msg = "\n\nWelcom to use TMS320F28335's SCI !";    scia_msg(msg);//先发送数据判断串口是否工作正常 // Enable interrupts required for this example    PieCtrlRegs.PIECTRL.bit.ENPIE = 1;   // Enable the PIE block    PieCtrlRegs.PIEIER9.bit.INTx1=1;     // PIE Group 9, int1    PieCtrlRegs.PIEIER9.bit.INTx2=1;     // PIE Group 9, INT2    PieCtrlRegs.PIEIER9.bit.INTx3=1;     // PIE Group 9, INT3    PieCtrlRegs.PIEIER9.bit.INTx4=1;     // PIE Group 9, INT4    IER = 0x100;        // Enable CPU INT    EINT;         while(1)         {         } } void error(void) {     asm("     ESTOP0"); // Test failed!! Stop!     for (;;); } interrupt void sciaTxFifoIsr(void) {         Uint16 i;         for(i=0;i<12;i++)         {                 SciaRegs.SCITXBUF=m;         } //        ScibRegs.SCIFFTX.bit.TXFFINTCLR=1;//清楚发送FIFO中断标志位。在此处如果清零会导致发送FIFO中断一直产生,因此要关闭才行         PieCtrlRegs.PIEACK.bit.ACK9=1; } interrupt void sciaRxFifoIsr(void) {         Uint16 i;         for(i=0;i<12;i++)         {                 m=SciaRegs.SCIRXBUF.all;         }         msg = "\n\nYou just sent the following characters : ";         scia_msg(msg);         scia_msg(m);         SciaRegs.SCIFFRX.bit.RXFFINTCLR=1; //        ScibRegs.SCIFFTX.bit.TXFFINTCLR=1;         PieCtrlRegs.PIEACK.bit.ACK9=1; } void scia_fifo_init() {     //SCI通信控制寄存器     SciaRegs.SCICCR.all =0x0007;   //SCI数据长度控制位111,8位数据                                    //空闲模式,禁止循环自检模式                                    //不允许奇偶校验                                    //一位停止位    //SCI控制寄存器1    SciaRegs.SCICTL1.all =0x0003;   // SCI发送使能,SCI接收使能                                    // Disable RX ERR, SLEEP, TXWAKE //   ScibRegs.SCICTL2.bit.TXINTENA =1; //   ScibRegs.SCICTL2.bit.RXBKINTENA =1;    SciaRegs.SCIHBAUD    =0x0001;    SciaRegs.SCILBAUD    =0x00E7;//设置波特率为9600    //SCI发送FIFO寄存器SCIFFTX    SciaRegs.SCIFFTX.bit.SCIFFENA=1;//使能FIFO    SciaRegs.SCIFFTX.bit.SCIRST=1;//SCI复位标志位,1.SCI接收和发送FIFO功能继续工作    SciaRegs.SCIFFTX.bit.TXFFIENA=0;//不使能发送FIFO中断    SciaRegs.SCIFFTX.bit.TXFFIL=0x0C;//12级FIFO使能    SciaRegs.SCIFFTX.bit.TXFFINTCLR=1;//发送FIFO中断清除标志位,1,清除TXFFINT位    SciaRegs.SCIFFTX.bit.TXFIFOXRESET=0;//SCI发送FIFO复位,0,复位发送FIFO指针    //SCI接收FIFO寄存器SCIFFRX    SciaRegs.SCIFFRX.bit.RXFFOVRCLR=1;//SCI接收FIFO溢出清除标志位,1,清除RXFFOVF    SciaRegs.SCIFFRX.bit.RXFFINTCLR=1;//接收FIFO中断清除标志位    SciaRegs.SCIFFRX.bit.RXFIFORESET=0;//SCI接收FIFO复位,0,复位接收FIFO指针    SciaRegs.SCIFFRX.bit.RXFFIENA=1;//接收FIFO中断使能位,    SciaRegs.SCIFFRX.bit.RXFFIL=0x0C;//12级FIFO使能    //SCI FIFO控制寄存器SCIFFCT    SciaRegs.SCIFFCT.all=0x00;    // SCI控制寄存器1    SciaRegs.SCICTL1.bit.SWRESET=1;//Relinquish SCI from Reset    SciaRegs.SCIFFTX.bit.TXFIFOXRESET=1;    SciaRegs.SCIFFRX.bit.RXFIFORESET=1; } void scia_xmit(int a)//单个字符发送代码 {     while (SciaRegs.SCICTL2.bit.TXRDY == 0) {}     SciaRegs.SCITXBUF=a; } void scia_msg(char * msg)//发送字符串代码 {     Uint16 len;     Uint16 i;     len=(strlen(msg)-1);     for(i=0;i<len;i++)     {             scia_xmit(msg);     } //   return 0; } //=========================================================================== // No more. //===========================================================================  

  • 发表了主题帖: DSP串口SCI最高效的接收和发送不定长度的方案

    本文介绍dsp高效接收和发送不定长度的数据,该方法减小cpu的中断次数,经过长时间检验,该方法安全可靠效率最大。我使用的是28034只有4级FIFO,在优化前中断深度设置为1,cpu频繁中断,导致正常程序受影响。经过改进,设置发送中断深度为0级,接收中断为4级,只有在数据发送完成和接收满4个字节中断。 接收流程为:等待接收中断—》读取4个fifo数据—》超时检查—》读取fifo剩余数据。这样就会接收完整的一帧数据。 发送流程:触发中断开始发送数据—》写入4个数据到发送fifo----》等到fifo为空中断–》写入下4个数据到fifo----》写入最后剩余数据。 具体如下: (1)初始化配置 void scia_init() {     // Note: Clocks were turned on to the SCIA peripheral     // in the InitSysCtrl() function      SciaRegs.SCICCR.all =0x0007;   // 1 stop bit,  No loopback                                    // No parity,8 char bits,                                    // async mode, idle-line protocol     SciaRegs.SCICTL1.all =0x0003;  // enable TX, RX, internal SCICLK,                                    // Disable RX ERR, SLEEP, TXWAKE     SciaRegs.SCICTL2.bit.TXINTENA = 1;//使能发送中断          SciaRegs.SCICTL2.bit.RXBKINTENA = 1;//使能接收中断                                  //BRR = (LSPCLK / (SCI Asynchronous Baud * 8)) - 1 ,LSPCLK=30M,     //BRR = 0x0020, baud=115200     //BRR = 0x0007, baud=460800     //BRR = 0x0061, baud=38400     SciaRegs.SCIHBAUD    =0x0000;     SciaRegs.SCILBAUD    =0x0020;//     SciaRegs.SCICCR.bit.LOOPBKENA =0; // Enable loop back     SciaRegs.SCICCR.bit.STOPBITS = 2; //     SciaRegs.SCIFFTX.all=0xC020;//设置发送中断为0级     SciaRegs.SCIFFRX.all=0x0024;//设置接收中断为4级     SciaRegs.SCIFFCT.all=0x0;     SciaRegs.SCIPRI.bit.FREE = 0;//仿真模式下是否自由运行     SciaRegs.SCICTL1.all =0x0023;     // Relinquish SCI from Reset     SciaRegs.SCIFFTX.bit.TXFIFOXRESET=1;  //发送FIFO复位     SciaRegs.SCIFFRX.bit.RXFIFORESET=1;   //接收FIFO复位     } (2)接收中断和发送中断 interrupt void SCI1_RXD_isr(void) {     UINT16 RxData;     //RxData = SciaRegs.SCIRXBUF.all;     //上一个数据帧应用层未取走,数据丢弃     if ((KeyBoardRxFrame.status == UART_FRAME_RX_END) || (KeyBoardRxFrame.index >= 40))     {         KeyBoardRxFrame.pdata[KeyBoardRxFrame.index++] = SciaRegs.SCIRXBUF.all;         KeyBoardRxFrame.pdata[KeyBoardRxFrame.index++] = SciaRegs.SCIRXBUF.all;         KeyBoardRxFrame.pdata[KeyBoardRxFrame.index++] = SciaRegs.SCIRXBUF.all;         KeyBoardRxFrame.pdata[KeyBoardRxFrame.index++] = SciaRegs.SCIRXBUF.all;     }else     {         KeyBoardRxFrame.pdata[KeyBoardRxFrame.index++] = SciaRegs.SCIRXBUF.all;         KeyBoardRxFrame.pdata[KeyBoardRxFrame.index++] = SciaRegs.SCIRXBUF.all;         KeyBoardRxFrame.pdata[KeyBoardRxFrame.index++] = SciaRegs.SCIRXBUF.all;         KeyBoardRxFrame.pdata[KeyBoardRxFrame.index++] = SciaRegs.SCIRXBUF.all;         KeyBoardRxFrame.ticker = KeyBoardRxFrame.spacetime;     }          SciaRegs.SCIFFRX.bit.RXFFOVRCLR=1;   // Clear Overflow flag     SciaRegs.SCIFFRX.bit.RXFFINTCLR=1;   // Clear Interrupt flag       PieCtrlRegs.PIEACK.all|=0x100;       // Issue PIE ack } interrupt void SCI1_TXD_isr(void) {     Uint16 i;     Uint16 len;     // 发送一帧数据没有完成     if(KeyBoardTxFrame.index + 4 < KeyBoardTxFrame.len)              {         SciaRegs.SCITXBUF = KeyBoardTxFrame.pdata[KeyBoardTxFrame.index++];         SciaRegs.SCITXBUF = KeyBoardTxFrame.pdata[KeyBoardTxFrame.index++];         SciaRegs.SCITXBUF = KeyBoardTxFrame.pdata[KeyBoardTxFrame.index++];         SciaRegs.SCITXBUF = KeyBoardTxFrame.pdata[KeyBoardTxFrame.index++];                  SciaRegs.SCIFFTX.bit.TXFFINTCLR=1;  // Clear SCI Interrupt flag清除后产生中断     }     else     {         //发送剩下的数据         len = KeyBoardTxFrame.len - KeyBoardTxFrame.index;         for(i = 0; (len <= 40) && (i < len); i++)         {             SciaRegs.SCITXBUF = KeyBoardTxFrame.pdata[KeyBoardTxFrame.index++];         }                  //一帧数据结束         if(KeyBoardTxFrame.len)         {             KeyBoardTxFrame.status = UART_FRAME_TX_IT_END;         }      }     PieCtrlRegs.PIEACK.all|=0x100;      // Issue PIE ACK } (3)读取不到4个字节的数据 void SCI1_RX_FIFO(UART_FRAME *pFrame) {     Uint16 maxNum = 0;     while(SciaRegs.SCIFFRX.bit.RXFFST && maxNum < 4)     {         pFrame->pdata[pFrame->index++] = SciaRegs.SCIRXBUF.all;         maxNum++;     } } (4)在应用程序需要2ms周期查询接收数据是否结束 static void KeyboardJudgeRxFinish(void) {     //超时或数据长度大于40一帧接收完成     if(((KeyBoardRxFrame.ticker <= 0)&&(KeyBoardRxFrame.index > 0)) || KeyBoardRxFrame.index >= 40)     {         SCI1_RX_FIFO(&KeyBoardRxFrame);         KeyBoardRxFrame.len = KeyBoardRxFrame.index;         KeyBoardRxFrame.status = UART_FRAME_RX_END;//数据接收完成,应用可以取走数据     }     if(KeyBoardRxFrame.ticker > 0)     {         KeyBoardRxFrame.ticker--;     } } (6)发送 void KeyboardRs485TxFrame(void) {     SCI1SetTxMode();     //如果当前帧正在发送,又重新请求发送一帧数据,则请求的数据丢弃     if(KeyBoardTxFrame.status == UART_FRAME_TX_ING)     {         return ;     }     //启动发送第一个字节后,产生中断,接着数据在中断中发送完成     KeyBoardTxFrame.index = 0;     SciaRegs.SCIFFTX.bit.TXFFINTCLR=1;//启动发送中断     KeyBoardTxFrame.status = UART_FRAME_TX_ING; }

  • 2020-12-07
  • 发表了主题帖: DSP的基本结构和特征

    编程DSP芯片是一种具有特殊结构的微处理器,为了达到快速进行数字信号处理的目的,DSP芯片一般都采用特殊的软硬件结构: (1) 哈佛结构。     DSP采用了哈佛结构,将存储器空间划分成两个,分别存储程序和数据。它们有两组总线连接到处理器核,允许同时对它们进行访问,每个存储器独立编址,独立访问。这种安排将处理器的数据吞吐率加倍,更重要的是同时为处理器核提供数据与指令。在这种布局下,DSP得以实现单周期的MAC指令。     在哈佛结构中,由于程序和数据存储器在两个分开的空间中,因此取指和执行能完全重叠运行。 (2) 流水线。     与哈佛结构相关,DSP芯片广泛采用2-6级流水线以减少指令执行时间,从而增强了处理器的处理能力。这可使指令执行能完全重叠,每个指令周期内,不同的指令都处于激活状态。 (3) 独立的硬件乘法器。     在实现多媒体功能及数字信号处理的系统中,算法的实现和数字滤波都是计算密集型的应用。在这些场合,乘法运算是数字处理的重要组部分,是各种算法实现的基本元素之一。乘法的执行速度越快,DSP处理器的性能越高。相比与一般的处理器需要30-40个指令周期,DSP芯片的特征就是有一个专用的硬件乘法器,乘法可以在一个周期内完成。 (4) 特殊的DSP指令。     DSP的另一特征是采用特殊的指令,专为数字信号处理中的一些常用算法优化。这些特殊指令为一些典型的数字处理提供加速,可以大幅提高处理器的执行效率。使一些高速系统的实时数据处理成为可能。 (5) 独立的DMA总线和控制器。     有一组或多组独立的DMA总线,与CPU的程序、数据总线并行工作。在不影响CPU工作的条件下,DMA的速度已经达到800MB/S以上。这在需要大数据量进行交换的场合可以减小CPU的开销,提高数据的吞吐率。提高系统的并行执行能力。 (6) 多处理器接口。     使多个处理器可以很方便的并行或串行工作以提高处理速度。 (7) JTAG(Joint Test Action Group)标准测试接口(IEEE 1149标准接口)。     便于对DSP作片上的在线仿真和多DSP条件下的调试。 (8) 快速的指令周期。     哈佛结构,流水线操作,专用的硬件乘法器,特殊的DSP指令再加上集成电路的优化设计,可是DSP芯片的指令周期在10ns以下。快速的指令周期可以使DSP芯片能够实时实现许多DSP应用。

  • 发表了主题帖: LF2407A的FLASH烧写问题的几点说明

    TI现在关于LF24x写入FLASH的工具最新为c2000flashprogsw_v112。可以支持LF2407、LF2407a、 LF2401及相关的LF240x系列。建议使用此版本。在http: //focus.ti.com/docs/tool/toolfolder.jhtml?PartNumber=C24XSOFTWARE上可以下载到这个工具。我们仿真器自带的光盘中也有此烧写程序。 在使用这个工具时注意: 一,先解压,再执行setup.exe。 二、进入cc中,在tools图标下有烧写工具; 1、关于FLASH时钟的选择,此烧写工具默认最高频率进行FLASH的操作。根据目标系统的工作主频重新要进行PLL设置。方法:先在advance options下面的View Config file中修改倍频。存盘后,在相应的目录下(tic2xx\algos\相应目录)运行buildall.bat就可以完成修改了。再进行相应的操作即可。 2、若是你所选的频率不是最高频率,还需要设定你自已的timings.xx来代替系统默认的最高频率的timings.xx。例如 LF2407a的默认文件是timings.40。Timings.xx可以利用include\timings.xls的excel工作表来生成。然后在advance options下面的View Config file中修改相应的位置。存盘后,在相应的目录下运行buildall.bat就可以完成修改了。 3、对于TMS320LF240XA系列,还要注意:由于这些DSP的FLASH具有加密功能,加密地址为程序空间的0x40-0X43H,程序禁止写入此空间,如果写了,此空间的数据被认为是加密位,断电后进入保护FLASH状态,使FLASH不可重新操作,从而使DSP报废,烧写完毕后一定要进行Program passwords的操作,如果不做加密操作就默认最后一次写入加密位的数据作为密码。 4、2407A不能用DOS下的烧写软件烧写,必须用c2000flashprogsw_v112软件烧写; 5、建议如下:   1)、一般调试时,在RAM中进行;   2)、程序烧写时,避开程序空间0x40-0x43H加密区,程序最好小于32k;   3)、每次程序烧写完后,将word0,word1,word2,word3分别输入自己的密码,再点击 Program password,如果加密成功,提示Program is arrayed,如果0x40-0x43h中写入的是ffff,认为处于调试状态,flash不会加密;   4)、断电后,下次重新烧写时需要往word0~word3输入已设的密码,再unlock,成功后可以重新烧写了; 6、VCPP管脚接在+5V上,是应直接接的,中间不要加电阻。 7、具体事宜请阅读相应目录下的readme1,readme2帮助文件。 8.注意*.cmd文件的编写时应该避开40-43H单元,好多客户由于没有注意到这里而把FALSH加密。

  • 发表了主题帖: TI DSP GEL文件的功能?

    GEL文件的功能同emuinit.cmd的功能基本相同,用于初始化DSP。但它的功能比emuinit的功能有所增强,GEL在CCS下有一个菜单,可以根据DSP的对象不同,设置不同的初始化程序。以TMS320LF2407为例: #define SCSR1 0x7018 ;定义scsr1寄存器 #define SCSR2 0X7019 ;定义scsr2寄存器 #define WDKEY 0x7025 ;定义wdkey寄存器 #define WDNTR 0x7029 ;定义wdntr寄存器 StartUp() ; 开始函数 { GEL_MapReset(); ; 存储空间复位 GEL_MapAdd(0x0000,0,0x7fff,1,1); 定义程序空间从0000-7fff 可读写 GEL_MapAdd(0x8000,0,0x7000,1,1); 定义程序空间从8000-f000 可读写 GEL_MapAdd(0x0000,1,0x10000,1,1); 定义数据空间从0000-10000可读写 GEL_MapAdd(0xffff,2,1,1,1); 定义i/o 空间0xffff可读写 GEL_MapOn(); 存储空间打开 GEL_MemoryFill(0xffff,2,1,0x40); 在i/o空间添入数值40h *(int *)SCSR1=0x0200; 给scsr1寄存器赋值 *(int *)SCSR2=0x000C; 给scsr2寄存器赋值,在这里可以进行mp/mc方式的转换 *(int *)WDNTR=0x006f; 给wdntr寄存器赋值 *(int *)WDKEY=0x055; 给wdkey寄存器赋值 *(int *)WDKEY=0x0AA; 给wdkey寄存器赋值 }  

  • 发表了主题帖: 对于C5000,大于48K的程序如何BOOT?

    对于C5000,片内的BOOT程序在上电后将数据区的内容,搬移到程序区的RAM中,因此FLASH必须在RESET后放在数据区。由于C5000,数据区的空间有限,一次BOOT的程序不能对于48K。解决的方法如下: 1.在RESET后,将FLASH译码在数据区,RAM放在程序区,片内BOOT程序将程序BOOT到RAM中。 2.用户初试化程序发出一个I/O命令(如XF),将FLASH译码到程序区的高地址。开放数据区用于其它的RAM。 3.用户初试化程序中包括第二次BOOT程序(此程序必须用户自己编写),将FLASH中没有BOOT的其它代码搬移到RAM中。 4.开始运行用户处理程序。

  • 发表了主题帖: 如何编写C2000片内Flash?

    DSP中的Flash的编写方法有三中: 1.通过仿真器编写:在我们的网页上有相关的软件,在销售仿真器时我们也提供相关软件。其中 LF240x的编写可以在CCS中加入一个插件,F24x的编写需要在windows98下的DOS窗中进行。具体步骤见软件中的readme。有几点需要注意: a.必须为MC方式; b.F206的工作频率必须为20MHz; c.F240需要根据PLL修改C240_CFG.I文件。建议外部时钟为20MHz。 d.LF240x也需要根据PLL修改文件。 d.如果编写有问题,可以用BFLWx.BAT修复。 2.提供串口编写:TI的网页上有相关软件。注意只能编写一次,因为编写程序会破坏串口通信程序。 3.在你的程序中编写:TI的网页上有相关资料。

  • 发表了主题帖: 如何选择DSP?

    选择DSP可以根据以下几方面决定: 1)速度: DSP速度一般用MIPS或FLOPS表示,即百万次/秒钟。根据您对处理速度的要求选择适合的器件。一般选择处理速度不要过高,速度高的DSP,系统实现也较困难。 2)精度: DSP芯片分为定点、浮点处理器,对于运算精度要求很高的处理,可选择浮点处理器。定点处理器也可完成浮点运算,但精度和速度会有影响。 3)寻址空间: 不同系列DSP程序、数据、I/O空间大小不一,与普通MCU不同,DSP在一个指令周期内能完成多个操作,所以DSP的指令效率很高,程序空间一般不会有问题,关键是数据空间是否满足。数据空间的大小可以通过DMA的帮助,借助程序空间扩大。 4)成本: 一般定点DSP的成本会比浮点DSP的要低,速度也较快。要获得低成本的DSP系统,尽量用定点算法,用定点DSP。 5)实现方便: 浮点DSP的结构实现DSP系统较容易,不用考虑寻址空间的问题,指令对C语言支持的效率也较高。 6)内部部件:根据应用要求,选择具有特殊部件的DSP。如:C2000适合于电机控制;OMAP适合于多媒体等。 要了解DSP芯片的性能,本网中的"DSP及相关器件"中有介绍。  

  • 2020-12-05
  • 发表了主题帖: DSP 2812: 使用C++封装GPIO

    2812中的GPIO被分成很多组:GPA,GPB,GPD,GPE,GPF,GPG。 我们将组进行抽象。 组抽象:CGpio类 使用命名空间对类进行包裹 namespace NF281x{ class CGpio{     CGpio( const CGpio& ); public:     CGpio( volatile unsigned int& mux,volatile unsigned int& dir,             volatile unsigned int& dat,volatile unsigned int& set,volatile unsigned int& clr,             volatile unsigned int& tog ); 这里对拷贝构造进行了限制。 一下包含了相关寄存器的引用。定义这些寄存器,是为了在头文件中暴露出来,可以使用内联函数访问到。在一些需要快速操作的应用中,这样的设计是必要的。 protected:     volatile unsigned int& m_mux;     volatile unsigned int& m_dir;     volatile unsigned int& m_dat;     volatile unsigned int& m_set;     volatile unsigned int& m_clr;     volatile unsigned int& m_toggle; }; } 对MUX进行封装     /**      * 获取该组GPIO第i个io口的MUX      * @param i 0~15      * @return      * - 0 作为IO      * - 1 作为外设      */     inline unsigned int getMux( const int& i )const{         return m_mux&(0x0001<<i);     }     inline bool isMuxIo( const int& i )const{         return getMux(i)==0;     }     inline bool isMuxPeripheral( const int& i )const{         return getMux(i)!=0;     }     /**      * 设置GPIO作为IO      * @param i      */     void setMuxIo( const int& i );     /**      * 设置GPIO为外设模式      * @param i      */     void setMuxPeripheral( const int& i );     /**      * 设置GPIO的MUX      * @param i      * @param mux      * - 0 IO模式      * - 1 外设模式      */     inline void setMux( const int& i,const unsigned int& mux ){         if( mux==0 )             setMuxIo(i);         else             setMuxPeripheral(i);     } 对IO模式的操作进行封装     inline unsigned int getIoDir( const int& i )const{         return m_dir&(0x0001<<i);     }     inline void setIoDir( const int& i,const unsigned int& dir ){         if( dir==0 )             setIoIn(i);         else             setIoOut(i);     }     void setIoIn( const int& i );     void setIoOut( const int& i );     inline bool isIoIn( const int& i )const{         return getIoDir(i)==0;     }     inline bool isIoOut( const int& i )const{         return getIoDir(i)!=0;     } 对于DO进行封装     inline void set( const int& i ){         m_set |= (0x0001<<i);     }     inline void clear( const int& i ){         m_clr |= (0x0001<<i);     }     inline void toggle( const int& i ){         m_toggle |= (0x0001<<i);     } 子类CGpa,CGpb等 - 这里举一个例子,CGpa的类设计 namespace NF281x{ class CGpa:public CGpio{     CGpa( const CGpa& ); public:     CGpa(); }; }

  • 发表了主题帖: 电平变换的方法

    1,总线收发器(Bus Transceiver): 常用器件: SN74LVTH245A(8位)、SN74LVTH16245A(16位) 特点:3.3V供电,需进行方向控制, 延迟:3.5ns,驱动:-32/64mA, 输入容限:5V 应用:数据、地址和控制总线的驱动 2,总线开关(Bustch) 常用器件:SN74CBTD3384(10位)、SN74CBTD16210(20位) 特点:5V供电,无需方向控制 延迟:0.25ns,驱动能力不增加 应用:适用于信号方向灵活、且负载单一的应用,如McBSP等外设信号的电平变换 3,2选1切换器(1 of 2 Multiplexer) 常用器件:SN74CBT3257(4位)、SN74CBT16292(12位) 特点:实现2选1,5V供电,无需方向控制 延迟:0.25ns,驱动能力不增加 应用:适用于多路切换信号、且要进行电平变换的应用,如双路复用的McBSP 4,CPLD 3.3V供电,但输入容限为5V,并且延迟较大:>7ns,适用于少量的对延迟要求不高的输入信号 5,电阻分压 10KΩ和20KΩ串联分压,5V×20÷(10+20)≈3.3V

统计信息

已有1354人来访过

  • 芯币:10535
  • 好友:--
  • 主题:3109
  • 回复:727
  • 课时:--
  • 资源:19

留言

你需要登录后才可以留言 登录 | 注册


博浩元电子 2018-10-25
不错,干货
查看全部