灞波儿奔

  • 2019-09-14
  • 发表了主题帖: TI c6455 烧写概述

    以下介绍的是dsp c6455的一种烧写程序的方式。它是通过片内运行的程序,将待烧写的程序写入片外FLASH中。在DSP上电或复位后,执行EMIFA 8-bit ROM BOOT,从片外FLASH加载程序并运行。 6455的烧写过程涉及三部分的文件: 1.主程序文件:完成所需功能的那部分程序文件,包含main.c。编译后会被编译到.boot_load以后的段,  主函数被编译到 .cinit段。  2.搬运程序文件: 完成烧写后,6455上电或复位,会自动将片外flash的头1k字节搬运到片内SRAM,  并开始运行。这1k字节内的程序,即搬运程序,需要将主程序,从片外FLASH搬运至片内SRAM,最后跳转到主程序运行。  搬运程序文件由boot.c和boot.asm两部分组成,会被编译到.boot_load 段。  3.烧写程序文件:它的作用是将被编译下载到片内SRAM后的主程序和搬运程序,烧写到片外FLASH中。  需要另建一个工程 烧写过程:  1.建一个工程pro1,包含主程序文件和搬运程序文件。编译通过后,将程序下载到6455内(只下载,不执行).  查看debug/release目录下map文件,得到程序各段在片内SRAM中的位置。  eg:  .boot_load start:00800000 length:000000e0 .cinit start:009e0500 length:00000214 .switch start:009e0714 length:000000e8  ……  .csl_vect start:009f2c00 length:00000200 2.另建一个工程pro2,包含烧写程序文件。编写cmd文件,避开pro1已使用的片内SRAM的地址,  分配烧写程序文件被编译后各段在片内SRAM中地址。在上例中,可以将烧写程序文件被编译后的各段,  放在0x008000e0-0x009f0500之间的地址。编译下载并运行烧写程序,将之前片内SRAM中已被编译下载的  0x00800000-0x008000e0和0x009e0500-0x009f2c00间pro1的程序,写入FLASH中,从而完成烧写的过程。  烧写程序文件main.c中烧写的函数: //烧写搬运程序  boot_source= 0x00800000;  boot_length=0x000000e0;  for(i = 0; i < boot_length; i++)  {     WriteByte(i,*(unsigned char *)( boot_source+i));     C6455_wait(200); }  //烧写主程序  pragram_source=0x009e0500;  pragram_length=0x009f2c00 + 0x00000200 -0x009e0500 ;  for(i = 0; i < pragram_length; i++)  {     WriteByte(i+0x400,*(unsigned char *)( pragram_source+i));     C6455_wait(200); } 总结:以上只是6455烧写的一种方式的大概过程,让读者有个初步印象,详细过程需要读者进一步探索。  

  • 发表了主题帖: UDP协议点对点(P2P)通讯(或者说NAT穿越)实例

    【实验环境】 一个服务端Server,两个客户端:Client1和Client2 。Server和Client1在Linux系统上运行,Client2在Windows上运行。 Server端源代码: #include <stdio.h> #include <string.h>   #include <sys/types.h>   #include <sys/socket.h>   #include <netinet/in.h>   #include <arpa/inet.h>      int main(int argc, char *argv[])   {       int     i, n, len;     int     sockfd, port, sin_size;       struct     sockaddr_in addr;   //服务器网络地址结构体       struct     sockaddr_in remote_addr; //客户端网络地址结构体        char     buf[1024];  //数据传送的缓冲区            if(argc != 3)      {          fprintf(stderr,"Usage:%s ipaddr port\a\n",argv[0]);          return -1;      }      if((port = atoi(argv[2])) < 0)      {          fprintf(stderr,"Usage:%s ipaddr port\a\n",argv[0]);          return -1;      }           bzero(&addr,sizeof(struct sockaddr_in));      addr.sin_family=AF_INET;      addr.sin_addr.s_addr=inet_addr(argv[1]);//htonl(INADDR_ANY);       addr.sin_port=htons(port);            /*创建服务器端套接字--IPv4协议,面向无连接通信,UDP协议*/       if((sockfd=socket(PF_INET, SOCK_DGRAM, 0)) < 0)       {             perror("socket");           return -1;       }           /* 将套接字绑定到网络地址上 */       if (bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr)) < 0)       {           perror("bind");           return -1;       }         sin_size=sizeof(struct sockaddr_in);       printf("waiting for a packet...\n");              if((len = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&remote_addr,&sin_size)) < 0)       {           perror("recvfrom");           return -1;       }        printf("received packet from %s:%d\n", inet_ntoa(remote_addr.sin_addr), remote_addr.sin_port);            buf[len]='\0';       printf("%s\n",buf);            close(sockfd);       return 0;   }   Client1的源代码: #include <sys/types.h> #include <sys/socket.h> #include<pthread.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h>     int main(int argc, char **argv) {     int sockfd, port, client2_port, sin_size;       struct sockaddr_in addr;     struct sockaddr_in client2_addr;     int  i, n;     char buf[512];          if(argc != 5)      {          fprintf(stderr,"Usage:%s local_IPaddr local_port client2_IPaddr client2_port\a\n",argv[0]);          return -1;      }           if((port = atoi(argv[2])) < 0)      {          fprintf(stderr,"Usage:%s local_IPaddr local_port client2_IPaddr client2_port\a\n",argv[0]);          return -1;      }           if((client2_port = atoi(argv[4])) < 0)      {          fprintf(stderr,"Usage:%s local_IPaddr local_port client2_IPaddr client2_port\a\n",argv[0]);          return -1;      }           printf("This is a UDP client1\n");       bzero(&addr,sizeof(struct sockaddr_in));      addr.sin_family=AF_INET;      addr.sin_addr.s_addr=inet_addr(argv[1]);//htonl(INADDR_ANY);       addr.sin_port=htons(port);       if ((sockfd=socket(AF_INET, SOCK_DGRAM, 0)) <0)     {         printf("socket error\n");         return -1;      }       if (bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr)) < 0)       {           perror("bind");          close(sockfd);          return -1;       }         bzero(&client2_addr,sizeof(struct sockaddr_in));     client2_addr.sin_family = AF_INET;     client2_addr.sin_port = client2_port;  // 注意,在这里直接赋值,不要使用htons函数。(但是如果通讯失败,可以再用htons函数试试。)     client2_addr.sin_addr.s_addr = inet_addr(argv[3]);       i = 10;     while (i--)     {         sprintf(buf, "client1: hi %d", i);         n = sendto(sockfd, buf, strlen(buf), MSG_DONTWAIT, (struct sockaddr *)&client2_addr, sizeof(client2_addr));         if (n < 0)         {             printf("sendto error\n");             break;         }           sleep(1);     }          close(sockfd);          return 0; } 【测试方法】 1. 在远程机器上运行Server程序 参数是远程机器的本地IP和端口号(端口号随便写,只要不冲突就行),如下: # ./server 48.89.13.216 5060 waiting for a packet... 2. 在本地机器运行Client2(即TCP/UDP调试工具)  

  • 发表了主题帖: 无线通信协议有哪些

    本帖最后由 灞波儿奔 于 2019-9-14 22:56 编辑          用于实现计算机与网络连接之间的标准,网络如果没有统一的通信协议,电脑之间的信息传递就无法识别。 通信协议是指通信各方事前约定的用心规则,我们可以简单地理解为各计算机之间进行相互会话所使用的共同语言.两台计算机在进行通信时,必须使用的通信协议。 通信协议的种类和特点        目前常见的通信协议主要有:NetBEUI、IPX/SPX、NWLink、TCP/IP,在这几种协议中用得最多、最为复杂的当然还是TCP/IP协议,最为简单的是NetBEUI协议,它简单得不需要任何设置即可成功配置。   1、 NetBEUI协议         NetBEUI协议它的全称是:NetBIOS Extend User Interface,即用户扩展接口,它是由IBM于1985年公司开发的,它是一种体积小、效率高、速度快的通信协议,同时它也是微软最为喜爱的一种协议。它主要适用于早期的微软操作系统如:DOS、LAN Manager、Windows3.x和Windows for Workgroup,但微软在当今流行的WIN9X和WINNT中仍把它视为固有缺省协议,由此可见它并不是我们所认为是“多余”的,而且在有的操作系统中连网还是必不可少的,如在用WIN9X和WINME组网进入NT网络时一定不能仅用TCP/IP协议,还必需加上“NetBEUI”协议,否则就无法实现网络连通,不信试试看!   因为它的出现比较早,也就有它的局限性,NetBEUI是专门为几台到百多机所组成的单段网络而设计的,它不具有跨网段工作的能力,也就是说它不具有“路由”功能,如果您在一服务器或工作站上安装了多个网卡作网桥时,将不能使用NetBEUI作为通信协议,这一点必需记清楚!         NetBEUI通信协议的特点就是:a、体积小,因原来就要是DOS、LAN Manger等较低版本的操作系统,故它对系统的要求不高,运行后占用系统资源最少;b、上面已讲过,也恐是因为主要服务的对象较低版本的操作系统,它不具有路由功能,不能实现跨网络通信;c、因为简单,对系统要求低,也就适合初学组网人员学习使用。   2、 IPX/SPX协议        IPX/SPX协议的全称为:Internetwork Packet Exchange/Sequences Packet Exchange,网际包交换/顺序包交换。它是NOVELL公司为了适应网络的发展而开发的通信协议,它的体积比较大,但它在复杂环境下有很强的适应性,同时它也具有“路由”功能,能实现多网段间的跨段通信。当用户接入的是NetWare服务器时,IPX/SPX及其兼容协议应是最好的选择。但如在Windows环境中一般不用它,特别要强调的是在NT网络和WIN9X对等网中无法直接用IPX/SPX进行通信。   IPX/SPX的工作方式较简单,不需要任何配置,它可通过“网络地址”来识别自己的身份。在整个协议中IPX是NetWare最底层的协议,它只负责数据在网络中的移动,并不保证数据传输是否成功,而SPX在协议中负责对整个传输的数据进行无差错处理。在NT中提供了两个IPX/SPX的兼容协议:NWLink IPX/SPX 兼容协议、NWLink NetBIOS,两者统称为NWLink 通信协议。它继承了IPX/SPX协议的优点,更适应了微软的操作系统和网络环境,当需要利用Windows系统进入NetWare服务器时,NWLink通信协议是最好的选择。   3、 TCP/IP协议   TCP/IP协议的全称是:Transmission Control Protocol /Internet Protocol,即传输控制协议/网际协议。它是微软公司为了适应不断发展的网络,实现自己主流操作系统与其它系统间不同网络的互连而收购开发的,它是目前最常用的一种协议(包括INTERNET),也可算是网络通信协议的一种通信标准协议,同时它也是最复杂、最为庞大的一种协议。TCP/IP协议最早用于UNIX系统中,现在是Internet的基础协议。   TCP/IP通信协议具有很灵活性,支持任意规模的网络,几乎可连接所有的服务器和工作站,正因为的灵活性也带来了它的复杂性,它需要针对不同网络进行不同设置,且每个节点至少需要一个“IP地址”、一个“子网掩码”、一个“默认网关”和一个“主机名”。但是在局域网中微软为了简化TCP/IP协议的设置,在NT中配置了一个动态主机配置协议(DHCP),它可客户端自动分配一个IP地址,避免了出错。   TCP/IP通信协议当然也有“路由”功能,它的地址是分级的,不同于IPX/SPX协议,这样系统就很容易找到网上的用户,IPX/SPX协议用的是一种广播协议,它经常会出现广播包堵塞,无法获得最佳网络带宽。但特别要注意的一点就是在用WIN9X和WINME组网进入NT网络时一定不能仅用TCP/IP协议,还必需加上“NetBEUI”协议,否则就无法实现网络连通 TCP/IP协议集包括应用层,传输层  网络层 网络访问层  应用层:   包括:   超文本传输协议(HTTP):万维网的基本协议.   文件传输(TFTP简单文件传输协议):   远程登录(Telnet),提供远程访问其它主机功能,它允许用户登录     internet主机,并在这台主机上执行命令.    网络管理(SNMP简单网络管理协议),该协议提供了监控网络设备的方法,以及配置管理,统计信息收集,性能管理及安全管理等.   域名系统(DNS),该系统用于在internet中将域名及其公共广播的网络节点转换成IP地址. 网络层:    Internet协议(IP)     Internet控制信息协议(ICMP)    地址解析协议(ARP)    反向地址解析协议(RARP)  网络访问层:网络访问层又称作主机到网络层(host-to-network).网络访问层的功能包括IP地址与物理地址硬件的映射,以及将IP封装成帧.基于不同硬件类型的网络接口,网络访问层定义了和物理介质的连接. IPX Internet 网络分组交换协议(Internetwork Packet Exchange protocol)。第三层路由选择和网络协议。当某设备与不同网络的本地机建立通信连接,IPX 通过任意中间网络向目的地发送信息。IPX 类似于 TCP/IP 协议组中的 IP 协议。 SPX 序列分组交换协议(Sequenced Packet Exchange protocol)。传输层(第四层)控制协议,提供可靠的、面向连接的数据报传输服务。SPX 类似于 TCP/IP 协议组中的 TCP 协议。

  • 2019-09-12
  • 发表了主题帖: 一种基于嵌入式的USB读写器设计

             随着USB技术和闪存技术的飞速发展,移动存储设备的速度和容量日新月异,但在工业控制的上位机和下位机之间,数据传输依然采用传统的串并口技术,特别对一些前端的便携式采集系统,需要采集后临时存储,再拿回来传给作为上位机的PC机进行数据处理,给人们带来很多不变,为此,本文提出了一种方法--利用U盘的便捷特性开发一种基于嵌入式的USB读写器,方便地将采集数据以文件方式写入U 盘,PC机不需要任何特殊驱动便可以完成对数据的处理回放。利用本方法可以彻底解决下位机与PC机之间的数据传输难的问题。   1 硬件设计   系统采用TI公司的MSP430F149作为MCU,Cypress公司的SL811HS作为USB设备控制器,充分发挥各自优势满足低功耗要求。   1.1 MSP430   TI公司的 MSP430系列单片机是一种超低功耗的混合信号控制器,针对不同的应用而由各种不同的   模块组成,这些微控制器可用电池工作,且使用的时间很长,MSP430单片机具有16位RISC结构,CPU中的16个寄存器和常数发生器使其能达到最高的代码效率,灵活的时钟源可以使器件的功率消耗最低,数字控制的振荡器(DCO)可使器件从低功耗模式迅速唤醒,在短于6μs的时间内激活到活跃的工作方式,非常的内/外设和I/O端口资源,端口P1和P2支持中断唤醒。   1.2 SL811HS   Cypress公司的SL811HS是一款超低功耗的USB主/从设备控制器,主设备或从设备模式由软件控制,USB主/从设备控制器支持全高速和低速数据传输,内部256字节的SRAM缓存,访问时地址自动加1,可大大缩短读写指令周期,具有标准的8位双向微处理器总线接口,其中从设备模式下支持DMA。   1.3 硬件系统框图      USB读写器结构框图如图1所示,主要由MSP430F149和SL811HS构成,MSP430支持串口输入和14位的并口输入,P2.7用作中断唤醒位,P2.6用作标准位区分数据和命令,通过P4口向SL811发送控制信号,P6用作普通的双向总线接口与SL811进行数据交换,P1.1支持中断唤醒接收SL811的中断请求。   2 软件设计   为了使上位PC机能够直接读取该读写器写入U盘的数据,数据存储按照FAT32文件管理方式存储,其中涉及USB和UFI协议以及FAT32文件存储格式。   2.1 USB协议   USB(通用串行总线)用于将USB接口的外围设备(device)连接到主机(host),实现二者之间数据传输的外部总线结构,是一种快速、灵活的总线接口,USB的传输类型有控制(control)、批量(bulk)、中断(interrupt)和同步(synchronous)传输4种,它最大的特点是易于使用,即插即用,主要是用在中速和低速的外设。   控制数据用于在USB接入总线时对其进行配置,其他的驱动软件可以根据具体的应用来选择使用控制传输,这种数据传输不会丢失数据。   典型的批量数据包括象使用打印机或扫描仪时所出现的大数据量的数据,这种批量数据是连续的,通过在硬件中实现差错检测功能,并且有选择地进行一定的应进重试操作,可以在硬件层次上保证数据的可靠交换。   由设备自发产生的数据传输是中断数据传输,这类数据传输可以由USB设备在任意时刻发起,而且USB总线以不低于设备说明的速率进行传输。   同步数据在产生、传送和处理过程中是连续的和实时的,在稳定的同步数据发送和接收速率中包含了相应的时钟信息,为了保持定时关系,同步数据必须按照接收的速率进行传输。   2.2 BULK_ONLY和UFI协议   USB设备分为5大类,即显示器、通信设备、音频设备、人机输入和海量存储。通常所用的U盘、移动硬盘均属于海量存储类。海量存储类的规范中包括4个独立的子规范,即CBI传输、Bulk-Only传输、ATA命令块、UFI命令规范。前两个协议定义了数据/命令/状态在USB总线上的传输方法,Bulk-Only传输协议仅仅使用Bulk端点传送数据/命令/状态,CBI传输协议则使用Control /bulk/interrupt三种类型的端点进行数据/命令/状态的传送。后两个协议定义了存储介质的操作命令,ATA协议用于硬盘,UFI协议则针对 USB移动存储,U盘读写器的设计遵循Bulk-Only传输协议和UFI命令规范。UFI命令块规范是针对USB移动存储而制定的,它总共定义了19个12字节长度的操作命令。   Bulk-Only事务以主机向设备发送CBW(Command Block Warp)包,并以建立相应的数据传输开始的,设备接收到CBW包,检查并解释它,试图满足主机的要求,并通过CSW(Command State Wrap)包向主机返回状态信息。   CBW是主机通过Bulk-Out端点向设备发送的命令块包,在CBW中使用方向位和数据传输长度域指明期待的传输,CBW必须起始于包边界,并且必须以31字节的短包传输   结束,相继的数据包和CSW包必须开始于一个新的包边界,所有的CBW包必须按低字节在前的次序传输。   CBW包结构如图2所示,各域含义如下:     1)命令块包标识。CBW包标记,表明这是一个CBW包,这个域的值为43425355H。   2)命令块标记。当设备返回相应的CSW包时,必须使命令状态标记域的值与此值相同。   3)数据传输长度。指明命令执行期间在Bulk端点上传数据的字节长度,如果这个域的值是0,则在CBW和CSW之间设备和主机不传输任何数据,并且设备将忽略在命令块标旗域中的方向位的值。   4)命令块标旗。方向位规定了Bulk端点数据传输的方向,其他位预留。   5)逻辑单元号。指定命令块被发送到的逻辑单元号,如果设备不支持多个逻辑单元号,则主机将这个域设置为0。   6)CBWCB长度,定义了CBWCB的有效长度,合法值为1-16。   7)CBWCB。由设备执行的命令,由设备解释。   CSW向主机表明来自于CBW包的命令块的执行状态。设备收到CBW包解析处理后将通过Bulk-In端点发送一个CSW包。   CSW开始于包边界,并以13字节的短包结束,结构如图3所示,各域含义如下:   1)命令状态包标识。CSW包的标记,表明这是一个CSW包,这个域的值为53425355H。   2)命令状态标记。次域的值域CBW包的命令块标记相同。   3)数据残余。实际数据传输量与CBW包中规定的数据传输长度的差值。   4)命令执行状态,表明命令成功或失败信息,如果命令执行成功,则设备将设置此域的值为0,非0值;则表明失败或错误。   UFI是针对USB移动存储而制定的命令块协议,它规定了主机和设备进行信息交换所使用的命令块、数据和状态信息,Bulk-Only传输协议定义了传输这些信息的方法,其中 UFI命令块是封装在CBW包中的CBWCB,设备通过读取CBWCB确定具体要执行何种操作命令(如读命令),如何完成这个命令(如从闪存的哪个地址读,需要读取的长度),设备将命令的执行状态封装成CSW返回给主机。   UFI用于大多数命令的12字节命令块的描述,结构如图4所示,其中各参数意义如下:   1)操作命令代码。指明所需要执行的操作命令;   2)逻辑单元号。指明命令将发送到哪个逻辑单元,如果设备只有一个逻辑单元,则此域的值为0。   3)逻辑块地址。命令操作的起始地址。   4)传输长度,指明请求传输的数据量,通常以“扇区”作单位,但是有几个命令是以“字节”作单位的,对于这些命令,传输长度域可以以不同的名字标识,若此域的值为0,则表面没有数据需要传输。   5)参数列表长度,用于指定发送到设备的字节数,这个域典型的应用于发送到设备的参数命令块(如模式参数、诊断参数等),若此域的值为0,则表面没有数据需要传输。   6)分配长度,指明主机已经分配的用于返回数据的最大字节长度,若此值为0,则表明没有数据需要传输。     2.3 FAT32   FAT是Microsoft较早推出的文件系统,具有高度的兼容性,目前仍然广泛应用于个人电脑尤其是移动存储设备中,FAT由引导扇区、FAT1表、FAT2表、目录和文件区组成(其中FAT2表是FAT1表的备份)。磁盘的管理是以扇区为单位的,而移动存储设备则是以块为单位的,FAT将块映射成扇区,原理相同,FAT将磁盘空间以一定数目的扇区为单位进行划分,这样的单位成为“簇”。通常情况下,每扇区 512字节的原则是不变的,簇的大小一般是2n(n为整数)个扇区的大小。所以以簇为单位而不以扇区为单位进行磁盘的分配   ,是因为当区分容量较大时,采用512字节的扇区管理,会增加FAT表的项数和大文件存取的消耗,使文件系统效率不高。   引导扇区DBR(DOS Boot Record)通常占用分区的第0扇区,共512字节,FAT表紧随其后。DBR的第一部分是一个x86跳转指令、厂商标志和操作系统版本号,接下来的从偏移0x0B开始的是一段描述能够使可执行引导代码找到相关参数的消息,通常称之为BPB。最后是引导程序代码以及扇区结束标志。BPB中记录了扇区大小、簇的扇区数、保留扇区数、FAT表大小和文件系统类型等重要参数,用于文件的索引和定位计算。   2.4 软件系统模块   USB读写器软件由主模块、USB模块、BULK模块、FAT模块和中断处理模块组成。各模块之间相互协调调用,共同完成对U盘文件的读写创建。   USB模块负责检测USB设备的移入/移出,对插入的设备进行枚举,分配设备地址端点号,配置设备接口端点描述符,建立BULK_ONLY输入/输出通道,BULK模块则在已建立的BULK_ONLY输入/输出通道发送CBW数据包,并接收CSW 数据包,通过CBW中嵌入的CBWCB信息确定对U盘读/写操作的扇区位置和大小,FAT模块主要完成簇和扇区间的索引定位,即在U盘内寻址,利用BPB 中的参数计算给定扇区的所在簇以及进入休眠状态,等待外部中断唤醒。   中断处理模块是在中断唤醒后根据中断类型进入不同功能子模块的,也是整个软件系统的核心部分。中断模块接收到串口或并口数据后,根据内部命令字要求进行解析。      3 应用测试结果   在人造金刚石压力机压力锤头裂纹检测中,采用声发射技术进行实时监控,一旦出现异常立即停机报警,为了对出现异常时的数据进一步分析,需要将现场数据带回研究室。利用基于嵌入式的U盘读写器可以免去现场布线的麻烦,还可以省去PC机端驱动软件的开发,直接将现场数据写入U盘,方便地利用PC机中安装的MATLAB进行数据的详细分析处理。   结语   基于嵌入式U盘读写器的设计实现可以方便地将下位机数据存储到普通U盘,彻底解决了工业控制中上下位机之间数据传输难的问题,也为以后其他领域的应用创造了前提条件,如长途客车中的黑匣子、家庭老人健康监测,随着USB技术和闪存技术的进一步发展,该设备应用领域将会逐步扩展,市场前景良好。

  • 发表了主题帖: 初识TMS320C5410

    今年学校开设了DSP的课程,于是开始跨入DSP领域,学校配的设备是北京百科融创的实验箱RC-DSP-II+,里面即有C2000系列的TMS320LF2407,主要用在控制方面;也有TMS320C5410,主要用在音频处理和通讯。结合自身的兴趣爱好,于是决定学习C5000系列。 这里的DSP是Digital Signal Processor数字信号处理器,它可以算是功能超强的单片机,然而学习起来却十分复杂,本人虽有较强的单片机功底,刚开始学的时候也是云里雾里,加之无导师指导,学习之路可谓十分艰苦,于是在图书馆借了N多书,看了网上的N多教程,摸爬滚打两个月之后,才渐渐有了些小领悟。   学习之前肯定要对芯片有一定的了解,可以网上介绍文章都是泛泛而谈,很少谈到重点,下面是我自己对这块芯片(TMS320C5410)的认识: (1) 16位定点DSP芯片,总线结构为改进的哈佛结构,内部有1个17X17位的乘法器; (2) 外接晶振最大为40MHz,通过PLL倍频最高主频为160MHz; (3) 片内ROM大小为16K×16bit , DARAM大小为8K×16bit, SARAM大小为56K×16bit; (4) 3组多通道缓冲串行口(McBSP),DMA,主机接口HPI (5) 通用IO引脚(BIO与XF),外部中断引脚INT0,INT1,INT2,INT3与NMI   有了以上基础知识,我们就可以来做我们的第一个实验:利用通用IO引脚XF来控制LED灯的亮与灭。拿到一块开发板,一般DSP的XF引脚都会连接一个LED灯,方便我们程序调试时用作指示灯,我们查看原理图,就可以看到类似如下的图: 图1.XF引脚与LED灯相连   那么,我们只要把XF引脚电平置0,即可点亮LED灯;置1即可熄灭LED灯。那怎么控制它呢?查看芯片的参考手册[ TMS320C54x DSP参考1(CPU and Peripherals) ],可以看到有这么一段:   图2. XF引脚的说明   大致意思是XF是状态寄存器ST1中的一位,而我们可以用SSBX指令来将XF置1,RSBX指令将XF清0。   下面就来建我们的第一个工程吧,我们的开发环境为CCS2.20,仿真器为XDS510,目标板为TMS320C5410,新建工程名为“1-LED_Control",并新建一个文件“main.c”,编写代码。   写好之后,连同“vector.asm"(即中断向量表),"5410.cmd" (存储器配置文件),"rts.lib"(C语言运行时库)文件一直添加到工程中去,编译,下载,运行,即可看到开发板上的LED开始一闪一闪的效果。置于中断向量表的编写与存储器如何配置,我将在第二讲详细介绍。   图3.总体工程图     图4.效果图1(灯亮) 图5.效果图2(灯灭)

  • 发表了主题帖: AT24C1024全容量读写NXPLPC11XX源程序

    AT24C1024全容量读写程序,只要读写起始地址和读写长度,程序自动跨页,绝对原创、绝对好用,含测试程序。 单片机源程序如下: #include "nxplpc11xx.h" #include "i2c.h" #include "AT24C1024.h" /************************************************************/ /* 函数功能:从AT24C1024中读数据                            */ /* 入口参数StartAddr:起始地址,经测试跨页时地址会自动加1。*/ /*           *pData:读入的数据放到这个数组中                */ /*           nBytes:要写的数据字节个数                     */ /************************************************************/ void AT24C1024_Read(uint32 StartAddr, uint16 nBytes, uint8 *pData) {                                     uint8 StartAddr_L, StartAddr_H, P0;     StartAddr_L = StartAddr & 0x0000ff;           StartAddr_H = (StartAddr >> 8) & 0x0ff;           P0 = (StartAddr / 0x10000) << 1;           I2C_Send_Ctrl(AT24C1024_Addr | P0);            I2C_Send_Byte(StartAddr_H);                   I2C_Send_Byte(StartAddr_L);                   I2C_Stop();             I2C_Send_Ctrl((AT24C1024_Addr | P0) + 1);             while(nBytes != 1)           {     *pData++=I2C_Recieve_Byte(1);                     // 应答位为ACK,不释放总线,继续读                 nBytes--;           }                 *pData++=I2C_Recieve_Byte(0);              // 应答位为NACK,停止读,释放总线                 I2C_Stop();                                // 产生一个停止条件                   delay_us(10);                              // 这个延时是任意地址读两段数据时最小时间间隔,保证停止位后正常产生下一个开始位。 }   /*******************************************************************/ /* 函数功能:给AT24C1024写数据                                     */ /* 入口参数StartAddr:起始地址,跨页写时,需要重新写入地址数据。  */ /*           *pData:把这个数组中的数据写入                         */ /*           nBytes:要写的数据字节个数                            */ /*******************************************************************/ void AT24C1024_Write(uint32 StartAddr, uint16 nBytes, uint8 *pData) {     uint8 StartAddr_L, StartAddr_H, P0;           while(nBytes != 0)                      // 所有数据发送完毕,退出循环           {           StartAddr_L = StartAddr & 0x0000ff;     // 计算低位地址           StartAddr_H = (StartAddr >> 8) & 0x0ff; // 计算高位地址           P0 = (StartAddr / 0x10000) << 1;        // 计算第17位,即P0     I2C_Send_Ctrl(AT24C1024_Addr | P0);     // 带P0发送器件地址           I2C_Send_Byte(StartAddr_H);                    // 发送高位地址           I2C_Send_Byte(StartAddr_L);                    // 发送低位地址           while(nBytes != 0)                      // 数据未发送完,循环           {           I2C_Send_Byte(*pData);                          pData++;     nBytes--;                                 StartAddr++;                 if((StartAddr & 0x00ff) == 0)           // 跨页,终止当前页循环,重新写入新地址                 break;           }                           I2C_Stop();                             // 产生一个停止条件                  delay_ms(3);                            // 这个延时为两次读写最小间隔时间,不要删除,连续大量写数据时有用。   }  } 复制代码 /************************************************/ /* AT24C1024全容量读写程序                      */ /* CPU型号:LPC1114FBD/302                      */ /************************************************/ #include "nxplpc11xx.h" #include "i2c.h" #include "AT24C1024.h" int main() { union Initial_Value{uint8 Val_Hex[400];float Val_Float[100];}Init;    //定义一个浮点数组,用于写入和读出,一个浮点数用4个字节       uint16 i;             float j;         SysCLK_config();         // 时钟配置   I2C_Init(0);             // 初始化I2C 快速模式   delay_ms(10);   for(i=0; i<100; i++)     // 初始化浮点数组分别为0——99。   {   j = i;   Init.Val_Float[i] = j;         }         AT24C1024_Write(0xff80, 400, Init.Val_Hex);   // 写入         delay_ms (3);         AT24C1024_Read(0xff80, 400, Init.Val_Hex);           while(1)         {}; }

  • 2019-09-11
  • 发表了主题帖: FPGA控制TMS320C6678上电复位程序

    module DSP_RST(         input clk_25m,         input RESETSTAT,                     //DSP复位状态 0表示复位态 1表示工作态         input LOCKED,                         //时钟模块是否正常         output ref LRESETNMIENz = 1'b0,       //局部复位管脚         output reg PORz=1'b0,                 //Power-on Reset         output reg RESETFULL=1'b0,            //Full Reset         output reg RESET=1'b0             );                  reg [2:0] RES_STATE=2'b00;         reg [15:0] por_counter=16'b0,resetfull_counter=16'b0,reset_counter=16'b0;                  parameter IDLE=2'b00,PULL_RESET=2'b01,PULL_POR=2'b10,PULL_RESETFULL=2'b11;          always @(posedge clk_25m)     if(LOCKED==1)         begin         case(RES_STATE)         IDLE:                   RES_STATE<=PULL_RESET ;         PULL_RESET: if(reset_counter<25000)                             begin                             reset_counter<=reset_counter+16'b1;                             RES_STATE<=RES_STATE;                             RESET<=1'b0;                             PORz<=1'b0;                             RESETFULL<=1'b0;                             end                         else                             begin                             RESET<=1'b1;                             LRESETNMIENz<=1`b1;    //禁止局部复位。                             RES_STATE<=PULL_POR;                             end         PULL_POR: if(por_counter<25000)                               begin                             por_counter<=por_counter+16'b1;                             RES_STATE<=RES_STATE;                             PORz<=1'b0;                             end                      else                             begin                             PORz<=1'b1;                             RES_STATE<=PULL_RESETFULL;                             end         PULL_RESETFULL:if(resetfull_counter<25000)                                 begin                                 resetfull_counter<=resetfull_counter+16'b1;                                 RES_STATE<=RES_STATE;                                 RESETFULL<=1'b0;                                 end                             else                                 begin                                 RESETFULL<=1'b1;                                 RES_STATE<=RES_STATE;                                 end         endcase                         end     else         begin         reset_counter<=16'b0;         resetfull_counter<=16'b0;         por_counter<=16'b0;         RES_STATE<=IDLE;         RESET<=1'b0;         PORz<=1'b0;         RESETFULL<=1'b0;         end endmodule  

  • 发表了主题帖: TMS320C6678的复位模式

     总共四种复位模式,前三种复位模式都会触发RESETSTAT,局部复位不会触发RESETSTAT. (1) 上电复位 (2) 硬件复位 (3) 软件复位 详见手册。 (4) 局部复位    上电复位后,可以进行局部复位,即对每个核单独复位。 局部复位可以被以下方式触发: LRESET pin Watchdog timer、CORESEL[3:0] and RSTCFG register LPSC MMRs(memory-mapped registers)   一般我们不使用局部复位,如果上电复位后,发现某些核处于复位状态,而其他核可以连接使用,多半是局部复位造成的,应该把LRESETNMIEN 设置为1,则可以避免这个问题。 )    

  • 发表了主题帖: TMS320C6678设备配置管脚和上电时序

    (1) LENDIAN :决定DSP的大小端。 (2) BOOTMODE[12:0] :决定DSP的自启动模式(详情见BootLoader for the C66x DSP User Guide)。 (3) PCIESSMODE : 决定PCIe子系统处于EP、legacy EP还是RC。 (4) PCIESSEN : 决定是否使能PICe 子系统,默认是不使能。 (5) PACKSEL: 决定网络协处理器的输入时钟是核时钟还是PASSCLK时钟。  DSP上电时序     DSP的上电时序,就是上电复位的时序。     设备初始化分为两个阶段: (1) 所有的供电电源稳定,不同的电源有供电时序,可见下面时序图。 (2) RESET、POR、RESERFULL按时序拉高,当然还包括时钟输入稳定。 注意: (1) 在电源稳定期间,POR要保持低电平,所以复位前拉低。 (2) DDRCLK、REFCLK应该在POR拉高前触发。 (3) 一旦获得DVDD18供电,RESETSTAT拉低。       在DVDD18供电前,所有的LVCMOS输入和双向管脚不能驱动为低电平或拉高。 (4) 在DVDD18有效后,RESETSTAT可以在任何时刻被拉高。在POR控制boot下,RESET必须在POR拉高前拉高。 (5) 在电源稳定后,POR必须持续保持低电平至少100us。至此,电源稳定阶段结束。 (6) 在电源稳定阶段后设备初始化需要500个REFCLK时钟周期。最大时钟周期是33.33nsec,所以在POR上升沿前延迟16us是必要的。在整个16us期间,时钟必须是激活的。 (7) 在POR稳定在高电平之后,RESETFULL必须要保持低电平24个REFCLK时钟周期。 (8) 在RESETFULL上升沿,设备锁定GPIO 配置管脚的电平,然后进行配置,到复位状态位RESETSTAT信号拉高延迟大约10000到50000个时钟周期。 (9) GPIO配置必须在RESETFULL上升沿前保持至少12个REFCLK时钟周期(transitions)。 (10) GPIO配置必须在RESETFULL上升沿后保持至少12个REFCLK时钟周期(transitions)。 总的来说:     在各个电源供电正常后,DSP的时钟稳定后,驱动RESET、POR、RESETFULL依次拉高,在RESETFULL的上升沿锁定DSP复位配置的GPIO电平,然后RESETSTAT拉高,则DSP上电复位完成。

  • 发表了主题帖: TMS320C6748的初始化GPIO步骤

    参看《SPRUFL8B》 2.9 Initialization部分 1.进行器件引脚复用设置 2.设置PSC(电源和睡眠控制寄存器)使能GPIO 3.设置方向、数据、中断控制寄存器来按需配置 四、介绍StarterWare的有关GPIO的库函数        使用StarterWare的GPIO函数需要添加“gpio.c”或者“C:\ti\C6748_StarterWare_1_20_04_01\binary\c674x\cgt_ccs\c6748\drivers\Debug\drivers.lib”,另外,还需要添加的头文件有"gpio.h"、 "soc_C6748.h"、 "hw_syscfg0_C6748.h"、 "hw_types.h" 这些头文件定义了很多寄存器的物理地址和用来计算地址位置等的函数宏。 1.按照步骤,我们先看一下有关复用的寄存器PINMUXn。因为我的开发板用到的是GPIO2[8],所以直接定位到PINMUX5寄存器: 看到GP2[8]在PINMUX5的31-28位,令此位为“1”即可。下图为PINMUX5寄存器的物理地址: 我们采取“读-屏蔽-写”的操作避免影响到其他位的配置。在hw_types.h中定义了HWREG(x)  (*((volatile unsigned int *)(x))),于是我们可以如下设置: PINMUX_5_VAL=HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(5));  /* 读取PINMUX5(复用寄存器)寄存器的值 */  PINMUX_5_VAL=(PINMUX_5_VAL&0x0fffffff)|0x80000000; HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(5))=PINMUX_5_VAL;/* 将设置后的值写回PINMUX5寄存器  GP2[8] */        上面的SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(5)值为0x1c14134正好为PINMUX5的地址。StarterWare会根据某一外设的寄存器的基址的规律来计算该外设下的某一寄存器地址,就像SOC_SYSCFG_0_REGS,它在soc_C6748.h的289行定义为0x01C14000,而PINMUX5在此外设下。而SYSCFG0_PINMUX(n)  (0x120 + (n * 4))正好利用了每个PINMUXn之间有九个其他寄存器,每个寄存器都是32位来线性计算的。 2.这里PSC用硬件复位后的default就好了。 3.设置输入/输出寄存器。库函数中定义了void GPIODirModeSet(unsigned int baseAdd, unsigned int pinNumber,unsigned int pinDir);来设置IO口的输入/输出状态。其中pinNumber要注意,函数入口参数中有如下说明: The 144 GPIO pins have serial numbers from 1 to 144.  *                       GPIO0[0] 1 ,GPIO1[0] 17  *                       GPIO2[0] 33 ,GPIO3[0] 49  *                       GPIO4[0] 65 ,GPIO5[0] 81  *                       GPIO6[0] 97 ,GPIO7[0] 113  *                       GPIO8[0] 129 譬如我想要设置GPIO2[8]为输出,则pinNumber=41。 GPIODirModeSet(SOC_GPIO_0_REGS, 41, GPIO_DIR_OUTPUT);可设置GPIO2[8]为输出;同样的,写GPIO函数一样的原理,只是寄存器地址计算不一样,这里就不列举了。GPIOPinWrite(SOC_GPIO_0_REGS, 41, GPIO_PIN_HIGH);就可以让GPIO2[8]输出高电平了。读操作具体函数参看"C:\ti\C6748_StarterWare_1_20_04_01\drivers\gpio.c”。这样我们就可以简单操作GPIO了!

  • 发表了主题帖: TMS320C6748的GPIO特性

     参考TI技术文档SPRUFL8B(《TMS320C674x/OMAP-L1x Processor GPIO User's Guide》)的1.2Features部分我们可以看出TMS320C6748的GPIO有如下特性:  1.可以通过单独的数据设置和清除寄存器来设置/清除GOIO功能和可通过软件在没有critical section保护下控制GPIO(这部分我不理解原文)  2.还支持通过写一个单一的输出数据寄存器设置/清除功能。  3.独立的输出/输入寄存器—— 输出寄存器可以读,以反映输出驱动器的状态;输入寄存器可以读,以反映引脚的状态。  4.所有GPIO信号可以作为中断源和可配置的边缘检测。  5.所有GPIO信号可以被用来产生到EDMA的事件。 TMS320C6748的GPIO框图

  • 2019-09-10
  • 发表了主题帖: DSPC6678的片上存储空间的分配机制

    嵌入式的设备如DSP上的栈空间是Kb级别,在函数内定义数组或申请空间都不能像linux下那样直接定义和申请,要么定义成全局的,要么指向一块划分好的空间,否则就会造成覆盖代码段等的问题。 片上有三片可读写的内存区域 L2:0x00800000-0x00880000        512Kb L3:0x0C000000-0x0C400000      4Mb DDR:0x80000000-0xFFFFFFFF  2Gb DSP的所有变量,函数,以及程序员定义的地址都保存在这三片空间上,程序员在定义变量时,若没有特殊规定,则编译器自动把变量分配到可读写空间上的任意位置,所以当程序员使用int *p = 0x00810000;这种语法的时候,很有可能会覆盖掉程序保存变量和函数的空间,导致程序运行异常,因此需要一个.cmd文件来约束,哪些地方用来给程序员自己定义变量地址用,哪些地方用来给程序为变量和函数申请内存来用。 /******************************************************************************  * Copyright (c) 2010-2011 Texas Instruments Incorporated - http://www.ti.com  *   *  Redistribution and use in source and binary forms, with or without   *  modification, are permitted provided that the following conditions   *  are met:  *  *    Redistributions of source code must retain the above copyright   *    notice, this list of conditions and the following disclaimer.  *  *    Redistributions in binary form must reproduce the above copyright  *    notice, this list of conditions and the following disclaimer in the   *    documentation and/or other materials provided with the     *    distribution.  *  *    Neither the name of Texas Instruments Incorporated nor the names of  *    its contributors may be used to endorse or promote products derived  *    from this software without specific prior written permission.  *  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT   *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT   *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,   *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT   *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  *   *****************************************************************************/ /*  *  Linker command file  *  */   -c -heap  0x8000 -stack 0x5000   /* Memory Map 1 - the default */ MEMORY {       L1PSRAM (RWX)  : org = 0x00E00000, len = 0x7FFF     L1DSRAM (RWX)  : org = 0x00F00000, len = 0x7FFF       L2SRAM (RWX)   : org = 0x00800000, len = 0x50000     L2SELFUSE      : org = 0x00850000, len = 0x30000     MSMCSRAM (RWX) : org = 0xc000000, len = 0x20000     DATASRAM       : org = 0xc100000, len = 0x200000    // FPGADATA       : org = 0xc300000, len = 0x20000     NOCACHE           : org = 0x50050000,    len = 30000h       DDR3DATA (RWX)  : org =0x80000000,len=0x10000000     DDR3 (RWX)     : org = 0x90000000,len = 0x10000000     DDR3_MID (RWX)     : org = 0xA0000000,len = 0x10000000 }   SECTIONS {     .csl_vect   >       L2SRAM//srio测试可以放在L2或者L3上     .text       >       L2SRAM//srio测试可以放在L2或者L3上        //代码段     GROUP (NEAR_DP)     {     .neardata                                                //已初始化的全局单变量     .rodata                                                    //带const修饰的已初始化的全局单变量     .bss                                                    //未初始化的全局单变量     } load > L2SRAM//srio测试可以放在L2     .stack      >       L2SRAM//srio测试可以放在L2            //栈     .cinit      >       L2SRAM//srio测试可以放在L2或者L3上        //程序初始化段     .cio        >       L2SRAM//srio测试可以放在L2            //printf用到的段     .const      >       L2SRAM//srio测试可以放在L2或者L3上        //带const修饰的全局多变量     .data       >       L2SRAM//srio测试可以放在L2或者L3上     .switch     >       L2SRAM//srio测试可以放在L2或者L3上        //switch表     .sysmem     >       MSMCSRAM//srio测试可以放在L2或者L3上    //堆,malloc     .far        >       L2SRAM//srio测试必须放在L2或者L3上        //未初始化的全局多变量     .testMem    >       L2SRAM//srio测试可以放在L2或者L3上     .fardata    >       L2SRAM//srio测试可以放在L2或者L3上        //已初始化的全局多变量     platform_lib >         L2SRAM//srio测试可以放在L2或者L3上     .L2  >         L2SRAM//srio测试可以放在L2或者L3上     .L3         >       MSMCSRAM     .NoCache       >   NOCACHE         //必须放在MSMCSRAM     .qmssSharedMem:          load >> MSMCSRAM     .cppiSharedMem:          load >> MSMCSRAM     .i2ceeprom:     load >> MSMCSRAM//i2ceeprom段必须放在MSMCSRAM     .emif16nandflash     load >> MSMCSRAM//emif16nandflash段必须放在MSMCSRAM     .srioSharedMem >       MSMCSRAM//srio放在msmc上的代码     .ChipIntc     >     MSMCSRAM //片级中断控制器的段     .Sharemem   >       MSMCSRAM //edma3多核测试main.c程序中的段     .timerSharedMem   >       MSMCSRAM //timer多核测试main.c程序中的段       //必须放在L2上     .srioL2Mem    >       L2SRAM//srio放在L2上的代码     .qmssL2Mem:          load >> L2SRAM//qmss放在L2上的代码     .cppiL2Mem:          load >> L2SRAM//cppi放在L2上的代码     .CoreIntcL2Mem:        load >> L2SRAM//coreintc放在L2上的代码     .edma3                load >> L2SRAM//edma3放在L2上的代码     .DDRDATA:               load>> DDR3DATA     .DDR3_MID:               load>> DDR3_MID }  MEMORY中定义的是所有内存的分配情况,SECTIONS定义的是每一块内存上定义了些什么东西。     //必须放在MSMCSRAM     .qmssSharedMem:          load >> MSMCSRAM     .cppiSharedMem:          load >> MSMCSRAM     .i2ceeprom:     load >> MSMCSRAM//i2ceeprom段必须放在MSMCSRAM     .emif16nandflash     load >> MSMCSRAM//emif16nandflash段必须放在MSMCSRAM     .srioSharedMem >       MSMCSRAM//srio放在msmc上的代码     .ChipIntc     >     MSMCSRAM //片级中断控制器的段     .Sharemem   >       MSMCSRAM //edma3多核测试main.c程序中的段     .timerSharedMem   >       MSMCSRAM //timer多核测试main.c程序中的段       //必须放在L2上     .srioL2Mem    >       L2SRAM//srio放在L2上的代码     .qmssL2Mem:          load >> L2SRAM//qmss放在L2上的代码     .cppiL2Mem:          load >> L2SRAM//cppi放在L2上的代码     .CoreIntcL2Mem:        load >> L2SRAM//coreintc放在L2上的代码     .edma3                load >> L2SRAM//edma3放在L2上的代码     .DDRDATA:               load>> DDR3DATA     .DDR3_MID:               load>> DDR3_MID 定义的是C6455库的内存分配方式,若要使用哪种库,就必须按照相应的规则去定义。  

  • 发表了主题帖: FPGA和DSP间基于SRIO的高速通信系统设计

         摘要: 现代信号处理系统通常需要在不同处理器之间实现高速数据通信,SRIO协议由于高效率、低延时的特性被广泛使用。本文研究了在FPGA和DSP两种处理器之间实现SRIO协议的方法,并通过电路设计和利用处理器的开发工具编程实现了两种处理器间的高速通信。经测试,该系统具有较高的传输效率。 引言        随着高性能信号处理系统对运算速度、通信速率等要求的不断提高,单独的处理器(如FPGA或DSP)无法满足高速实时信号处理的需求。TI公司的多核DSP处理性能强大,但是并行性不强,难以适应计算异常密集的应用,另外集成性的DSP接口也影响了数据传输的灵活性;FPGA具有极强的并行性,适合密集计算应用,而且可配置I/O和IP核支持多种数据传输接口,但FPGA的内部逻辑资源和存储资源有限,并且开发难度大,实现复杂算法也比较困难。因此,结合多核DSP和FPGA的优势,构建基于异构处理器的信号处理系统成为当前一种发展趋势。异构处理器间的高速通信成为高速信号处理系统[1]的关键问题之一,本文基于SRIO协议设计和实现了DSP与FPGA之间的高速数据通信。 1异构处理器电路 1.1DSP处理器         在处理器领域,多核DSP在处理性能、功耗和面积上都有很大优势,得到了广泛应用。TI公司的8核处理器TMS320C6678[2],基于KeyStone多核结构,具有高性能的浮点、定点计算能力,单核具有1 GHz的主频,运算速度可达320 GMACS/160 GFLOPS。该DSP采用同构多核架构,每个核可以独立地执行不同的计算任务,具有512 KB的私有内存。芯片具有4 MB共享内存供8个核心访问,而且具有SRIO、PCIe等多种接口,能够满足各种数据传输的需求。 1.2FPGA处理器       FPGA因其功能强大、接口灵活,成为当前的主流处理器之一,FPGA与DSP芯片有机结合不仅能够高效地实现复杂算法,而且还可以提高系统数据传输的效率和结构的灵活性。Xilinx公司Virtex6 LXT系列FPGA芯片XC6VLX550T,是一款具有高级串行数据传输功能的高性能逻辑器件,基于硬件GTX串行收发器,可以实现多种高速数据传输接口。采用SRIO IP核可以实现FPGA和DSP之间的SRIO协议通信。 1.3异构处理器电路互连       RapidIO[3]协议是一个开放的点对点分组交换标准,是面向嵌入式系统开发提出的高可靠、高性能、基于包交换的互连技术。串行RapidIO[4](SRIO)是采用串行差分模拟信号传输的RapidIO协议,基于SerDes(Serialize Deserialize)技术,采用差分交流耦合信号(具有抗干扰能力强、速率高、传输距离较远等优点),所以SRIO是一个针对嵌入式系统应用的高性能、低引脚数的高速互连接口。        SRIO协议分为3层:逻辑层、传输层和物理层。逻辑层定义了操作协议;传输层定义了包交换、路由和寻址机制;物理层定义了电气特性、链路控制和纠错重传等。SRIO是基于包交换的高速互连技术,其数据包是由包头、有效的数据载荷和16位CRC校验组成。包头的长度根据包类型不同,可能为十几到二十几个字节,最大的有效载荷长度为256字节。由于包长度短,所以传输延时较小,硬件上也易于实现,适合数字信号处理场合对传输延时要求较高的应用。        TMS320C6678集成了支持SRIOv2.1通信协议的4通道SRIO接口,可以实现每条通路1.25 Gbps、2.5 Gbps、3.125 Gbps、5 Gbps的通信速率。XC6VLX550T的GTX模块嵌入Serial RapidIO IP核,可支持线速率为1.25 Gbps,2.5 Gbps~3.125 Gbps,因此可实现异构处理器DSP与FPGA之间的SRIO高速串行通信。 为了最大程度地体现RapidIO串行接口的性能,本设计中采用3.125 Gbps的线速率,处理器之间采用4xSRIO连接方式, 1个1x接口即是一个差分对的一对读/写信号,一个4x接口即4个此类差分对的结合,因此采用4x SRIO连接可实现最高12.5 Gbps的数据传输速率。电路连接方式如图1所示,只需要将DSP的TX、RX端口与FPGA的RX、TX端口对应相接,由于SRIO采用差分线对实现数据传输,所以需要在异构处理器的RX端口的差分线上串联一个0.1 μF的电容,做交流耦合使用。 2 SRIO设计        DSP和FPGA作为SRIO连接的端点器件,两者可互为从属[5]。主设备需要管理通信的发起、配置、结束等一系列过程,从设备只需要被动地响应通信。基于DSP的编程比FPGA简便,为了降低开发难度和工作量,采用DSP作为主设备,是通信的发起端;FPGA作为从设备,是通信的目的端。 2.1 DSP端的SRIO配置         DSP端SRIO的软件设计基于SYS/BIOS操作系统,使用TI公司提供的多核软件开发套件(MCSDK),主要组件是开发平台中的芯片支持库(CSL)工具。CSL是TI公司为其DSP产品提供的API函数,提供了一个用于配置和控制片上外设的C语言接口,在程序设计过程中利用CSL库函数可以方便地访问DSP的寄存器和硬件资源,提高DSP软件的开发效率和速度。 2.1.1 SRIO初始化 实现SRIO重要的一步是SRIO的初始化,一般分为以下几步: ① 打开SRIO的电源和时钟:为了降低功耗,默认状态下SRIO模块的电源和时钟是处于关闭状态的,因此 SRIO 初始化首先要调用CSL_SRIO_OPEN函数将SRIO模块的电源和时钟打开。 ② 配置SRIO的串并转换器:将125 MHz的参考时钟通过串并转换器内部的锁相环倍频至1.25 GHz,串并转换器采用半速率时钟模式,利用这个时钟信号的上升沿和下降沿对4路8位数据分时移位输出,即每个时钟串并转换器的串行输出端将输出2位的数据,采用该模式降低了对时钟信号的要求,降低了电路设计难度。 ③ 设置4x工作模式:C6678有4个SRIO端口,将4路串并转换器使能。定义SRIO通信链路端点器件的ID,C6678提供了8个LSU模块用于SRIO数据操作的处理,每组LSU都有7个32位寄存器,通过配置LSUx_reg4将源器件DSP的ID设为0x00,目的器件FPGA的ID设计为0xFF。 ④ 等待SRIO初始化完成:通过配置SP_ERR_STAT寄存器,检测SRIO的端口状态是否OK,如果OK,则表示可以进行SRIO通信,否则提示初始化不成功或者其他情况导致不能通信。在SRIO初始化前需要FPGA端完成SRIO逻辑的配置,否则DSP在初始化SRIO期间无法和FPGA进行握手,会导致初始化失败。 2.1.2 SRIO的读写操作          SRIO初始化完成后,通过DSP对SRIO端口的读写操作实现和FPGA之间的数据传输。DSP读写支持的操作通过数据包格式中的Ftype和Ttype两个字段描述,I/O逻辑操作是简单实用的传输方式,使用该模式的前提是主设备要知道被访问端的存储器映射,可以直接读写从设备的存储器。I/O逻辑操作在被访问端的功能往往完全由硬件实现,所以被访问的器件不会有任何软件负担。表1所列为I/O操作的几种事务类型。本文使用的读操作事务是NREAD。在3种写操作事务中:NWRITE_R是带响应的写操作,效率较低;SWRITE要求数据载荷长度在8~256字节之间,且为8字节的整数倍。因此本文采用NWRITE写操作,配置简单且易于实现。   I/O逻辑操作使用了SRIO的功能模块LSU(Load Store Unit)和MAU(Memory Access Unit)。LSU实现I/O逻辑操作数据包的读写;MAU提取数据包中的源地址、目的地址、数据长度等信息,从而将数据包的有效数据载荷写入指定位置。DSP端SRIO的I/O逻辑操作可以分为4个部分: ① 锁定LSU寄存器:CSL_SRIO_IsLSUFull函数读取LSUx_reg6寄存器中的FULL位,为1,则LSU所有的影子寄存器已经写入配置文件等待数据发送,暂时没有可用的影子寄存器。 ② 配置寄存器:配置LSU寄存器0~4,获取传输信息,包括源地址dspAddress、目的地址rapidIOLSB、数据长度bytecount等,程序使用的函数是CSL_SRIO_SetLSUTransfer。 ③ 释放寄存器:完成锁定和配置LSU寄存器后,最后配置LSU寄存器5,确定数据包的事务类型,配置完成后通过CSL_SRIO_IsLSUBusy函数检测LSUx_reg6寄存器中的BUSY位。若BUSY为0,释放LSU控制权,该影子寄存器进入等待状态,最终将数据发送出去;若BUSY为1,则将数据存放在影子寄存器中,等待LSU完成当前传输至空闲再发送数据。 ④ 等待传输完成:通过CSL_SRIO_GetLSUCompletionCode函数读取寄存器SRIO_LSU_STAT_REG的状态,判断是否所有数据均传输完毕。 2.1.3 通信流程 C6678使用中断控制器(INTC)管理和分配多个外部中断源,其中有来自FPGA的中断源。本文中SRIO工作于主模式状态,FPGA通过GPIO向DSP发送中断,当DSP接收到来自FPGA的中断后,对FPGA相应的内存区域进行读写操作。本设计中,中断使用了GPIO8和GPIO9两个中断触发事件,分别将其映射到DSP的CPU中断4和中断5。在此状态下程序主要执行两种操作:在 DSP 收到中断4以后进入中断4服务函数, 完成从FPGA端读取数据的操作;在收到中断5以后进入中断5 服务函数,完成将数据写入FPGA端的操作。 2.2 FPGA端的SRIO配置 FPGA端的SRIO基于Xilinx公司的Serial RapidIO IP核[6]来实现,IP核底层硬件基于FPGA的GTX收发器。图2所示为FPGA端的SRIO实现结构,SRIO IP核左侧通过接口模块与用户逻辑相连,右侧通过输出引脚与DSP相连。IP核可划分为5个部分:RapidIO逻辑和传输层(LOGIC)模块、 RapidIO物理层(PHY)模块、RapidIO缓冲区(Buffer)模块、寄存器管理(Register Manager)模块、参考时钟和复位模块。根据不同的需求,用户可以选择使用物理层包封装(phy_wrapper)或者RapidIO包封装(rio_wrapper),本文选择使用RapidIO包封装。 本文以IP核为基础,采用已有的整体框架,围绕目标用户接口设计接口模块。中断作为FPGA和DSP之间的握手信号,FIFO作为用户逻辑和IP核之间的数据缓冲接口。图3所示为Rapid IO接口模块实现方案。 由于FPGA在通信中作为从设备,因此接口模块中不再需要IP核接口中发起用户的功能,只保留目标用户的功能,其中目标请求/响应状态机控制各模块的时序变化。接口模块左侧与用户逻辑接口相连,右侧与IP核目标用户接口相连。 中断机制部分,向DSP发送数据时采用发送FIFO的半满标志作为读中断,从DSP接收数据时采用接收FIFO的半空标志作为写中断。发送FIFO中数据超过一定量时触发DSP读数据,接收FIFO中数据低于一定量时触发DSP写数据。用户及时有效地控制FIFO的状态,可以保证FIFO不会被写满或者被读空。用户也可以产生中断逻辑,控制DSP对FPGA内部存储空间进行读写。本文引入了中断机制和数据缓冲FIFO,利于接口对接和功能拓展,实现数据在不同芯片之间的高效传输。 3 传输性能测试       本文对DSP与FPGA之间的SRIO通信进行性能测试。DSP的工作频率为1 GHz,SRIO 接口工作速率设置为3.125 Gbps,经过物理层8B/10B编码,数据包的实际传输速率为2.5 Gbps,传输方式设置为4x 模式,则理论数据传输速率应为10 Gbps。由于数据包的打包和解包等操作,实际速率会小于理论值。        表2是使用NWRITE和NWREAD对不同数据包进行通信速度测试的结果。在传输数据为32 字节时,考虑到数据包操作时的开销,与理论值比率仅为1.1%,很大一部分时间被花费在数据包的打包和解包的处理中,随着传输数据量的增加,SRIO的实际传输效率不断增大,最终维持在7 800 Mbps。经过多次反复实验,该统计结果稳定可靠,并且没有出现丢包误码的情况。 结语          本文针对当今高速信号处理系统对芯片间数据传输的需求,研究异构处理器DSP和FPGA间的数据传输技术。DSP端基于CSL库实现了SRIO的主设备通信,FPGA端基于RocketIO IP实现了从设备通信,并采用中断实现异构处理器之间的握手信号,经测试达到较高的传输速率。本文研究内容也适用于同系列的其他处理器之间的数据通信,具有较高的应用价值。

  • 发表了主题帖: 有关双口RAM存储器的几个问题

    本帖最后由 灞波儿奔 于 2019-9-10 21:56 编辑 双口RAM是常见的共享式多端口存储器,其最大特点是共享存储数据,即一个存储器配备两套***的地址线、数据线和控制线,允许两个***的CPU或控制器同时异步的访问存储单元。这种同时异步的访问存储单元需要内部仲裁控制逻辑的控制(BUSY功能输出),由于两个端口对双口RAM存取时存在以下4种情况: 1.两个端口不同时对同一地址单元存取数据; 2.两个端口同时对同一地址单元读出数据; 3.两个端口同时对同一地址单元写入数据; 4.两个端口同时对同一地址单元操作,一个写入数据,一个读出数据。 所以,内部仲裁控制逻辑相应的提供以下功能: 1.对同一地址单元访问的时序控制; 2.存储单元数据块的访问权限分配; 3.信令交换逻辑。 对同一地址单元访问的时序控制 当左右端口不对同一地址单元存取时,BUSY R=H,BUSY L=H,可正常存储;当左右端口对同一地址单元存储时,有一个端口的BUSY=L,禁止数据的存取,此时,两个端口中先出现的存储请求信号对应的BUSY=H,允许存储,后出现的存储请求信号对应的BUSY=L,禁止存储(注意:两端口间的存储请求信号出现时间差应满足仲裁最小时间间隔TAPS(IDT7132为5ns),否则仲裁逻辑无法判定哪一个端口的存储请求信号在前);在无法判定哪个端口先出现存储请求信号时,控制线BUSY L和BUSY R只有一个为低电平,不会同时为低电平,这样就避免了双端口存取出现错误。 存储单元数据块的访问权限分配 存储单元数据块的访问权限分配只允许在某一时间段内由1个CPU对自定义的某一数据块进行读写操作,这将有助于存储数据的保护,更有效地避免地址冲突。信号量(Semaphore,简称SEM)仲裁闭锁就是一种硬件电路结合软件实现访问权限分配方法。SEM单元是与存储单元无关的***标志单元,两个端口分别采用两个触发器可以实现这一功能。两个触发器在初始化时均使SEM允许输出为高电平,等待双方申请SEM,如果收到一方写入的SEM信号(通常低电平写入),仲裁电路将使其中一个触发器的SEM允许输出端为低电平,而闭锁另一个SEM允许输出端使其继续保持高电平。只有当先请求的一方撤消SEM信号,即写入高电平,才使另一SEM允许输出端的闭锁得到解除,恢复等待新的SEM申请。 信令交换逻辑(signaling logic) 为了提高数据的交换能力,有些双口RAM采用信令交换逻辑来通知对方。这类似与PC操作系统的进程同步的信箱机制。 以上是双口RAM自身提供的仲裁逻辑控制,也可采用自行设计的仲裁协议,比如FIFO。

  • 发表了主题帖: CPU对存储器的读写

    CPU对存储器的读写 计算机中专门有连接CPU和其他芯片的导线,通常称为总线。总线从物理上来讲,是一根根导线的集合。  根据传送信息的不同,总线从逻辑上分为三类。 地址总线、控制总线、数据总线 CPU  通过地址总线将需要读/写的地址信息发出。  通过控制线发出内存读/写命令,选中存储芯片,并通知它,要向其中读/写数据。  通过数据线将数据送入存储单元(读)或内存单元(写)

  • 发表了主题帖: dsp EMIF接口

    EMIF,全称是External Memory Interface,外部存储器接口,用于片外储存器扩展和外部数据接口的一种并行数据传输片上外设。它能与三种外部存储器无缝连接:同步突发静态存储器(SBSRAM),同步动态存储器(SDRAM),异步存储器(SRAM,ROM,FLASH)等。 这里补充一下它们的区别,虽然我觉得不是重点: SDRAM:读写操作要求与外部时钟同步;动态存储,芯片需要定时刷新;同步技术使得用于大容量、高速存储领域。 SBSRAM:支持同步突发访问,所以读写速度高;属于SRAM范畴,属于静态RAM,不需要刷新。 异步存储器:简单直接,时钟不必同步;但容量小,主要用于小容量数据存储和程序存储。 EMIF主要和EDMA和外设存储器连接通信,通过引脚。引脚中的CE片选引脚很重要,每个片选引脚对应一个CE空间。其它引脚如图: 虽然不同型号dsp寄存器名称有差别,但大体有如下最重要的EMIF寄存器: GBLCTL:EMIF全局控制寄存器 CECTL0/1/2/3:CE空间控制寄存器 SDCTL:SDRAM控制寄存器 SDTIM:SDRAM时序控制寄存器 SDEXT:SDRAM扩展控制寄存器 对于与SDRAM连接的配置过程: 配置开始-》全局控制寄存器配置EMIF时钟-》CE空间寄存器配置SDRAM空间-》SDCTL配置SDRAM工作模式-》SDTIM配置刷新模式-》配置扩展功能-》配置结束 对于与Flash连接的配置过程: 配置开始-》全局配置时钟,Flash接口选择-》CE空间读写时序控制-》配置结束 一般程序架构: #...宏定义 #include <csl.h> #include<csl_emif.h> ... void erase_Flash(); //函数声明 void write_Flash(); unsigned int read_Flash(); EMIF_Config myFlash = { //通过结构体配置 //配置全局寄存器 EMIF_GBLCTL_RMK( ... ), //配置CE寄存器1,但0、2、3忽略 EMIF_GECTL_DEFAULT, EMIF_GECTL_RMK( ... ), EMIF_GECTL_DEFAULT, EMIF_GECTL_DEFAULT, //忽略SDRAM相关寄存器 EMIF_SDCTL_DEFAULT, EMIF_SDTIM_DEFAULT, EMIF_SDEXT_DEFAULT,   } void main() {   CSL_init(); EMIF_config(&myFlash); erase_Flash(); //对flash而言,写入前先擦除 Flash_ptr = (unsigned int *)FLASH_BEGIN; //写入的初始地址 for(i = 0;i<DATA_SIZE;i++) { write_Flash(bootfile[i],Flash_ptr++); }   } void erase_Flash() { *FLASH_ADR2 = 0xaa; //擦除操作命令序列,flash特殊 *FLASH_ADR1 = 0x55; *FLASH_ADR2 = 0x80; *FLASH_ADR2 = 0xaa; *FLASH_ADR1 = 0x55; *FLASH_ADR2 = 0x10; } void write_Flash(unsigned int data,unsigned int addr) { *FLASH_ADR2 = 0xaa; //写操作命令序列,flash特殊,换作SDRAM则不用。 *FLASH_ADR1 = 0x55; *FLASH_ADR2 = 0xa0; *addr = data; } unsigned int read_Flash(unsigned int addr) { return (*addr); }  

  • 2019-09-07
  • 发表了主题帖: 嵌入式C代码优化

    之前刚开始工作时,参与做过嵌入式代码优化,除了最基本的函数实现细节算法优化外,还有一些细节的处理。当然之前优化时,也是借助了分析工具来分析哪些函数调用频繁,哪些开销比较大。对于具体细节的处理记得不一定全面了,当然也有部分操作在编译时,工具也有可能自动进行优化。 函数展开 类似inline,减少函数出栈入栈开销 结构体比较 相应数据结构具有不同的比特位含义,而高位更具有意义,比较时无需将成员一一比较,强转32/64位格式比较。 相同操作提取 提取相同深度的指针指向,如下A、C、D为指针,E为具体成员操作。 A->C->D->E1,A->C->D->E2,A->C->D->E3... ...则可将A1 = A->C->D,然后使用A1->E1、A1->E2、A1->E3进行替换操作。 消息合并 线程之前多个消息发送会导致开销变大,可以合并成单个消息,同时处理多个事情,当然前提是这些事件可进行合并。 时间空间的转换(动态申请用静态变量替代) 频繁使用的消息可以改为一次性申请或是静态方式,以减少频繁申请释放的开销;而同一逻辑部分可能申请多次的情况,可以采用半静态半动态的方式,可以通过统计经常同时使用的次数来确定静态内存的大小。 寄存器(变量定义)问题 在arm上汇编可以看到当定义uint8 i;for(i = 1; i< 255; i++)时寄存器为32bit,所以在处理8bit数据时,需要额外的移位等操作来放置溢出超过8位情况,此时使用uint32定义反而可以减少MIPS开销。

  • 发表了主题帖: MCU如何实现让部分代码运行在RAM中?

    MCU 异于 资源丰富的linux 平台。 MCU(如: 基于Cortex V6M 的Cortex M0+ 等) Code 通常运行在内嵌Flash 中。 在某些特定应用场合,需要将部分函数运行于RAM 中。 昨天,为解决次问题,实现了一种解法,具体做法如下: 1. 实现要运行在RAM的 routine, 本routine 使用纯汇编实现, 如: __asm void program_word2addr(uint32_t addr, uint32_t data) { push {r3, r4, r5, lr} ;save some regsiters /*your code for this routine*/ pop {r3, r4, r5, pc} } 2. 编译时,采用code 与运行位置无关的编译选项 如 (Keil --apcs /ropi/rwpi), 生成 *.axf; 3. 通过fromelf -c 将生成 *.axf 反汇编,找到对应program_word2addr 实现部分, 并将routine 对应的binary code Copy 到所要应用的 Code 中,以只读数组的形式出现: 如: const staic uint16_t s_flashProg2AddressCode[16] = {...., ....} 4. 定义 一个全局数组, 如 static uint16_t g_code[16], size正好等于 s_flashProg2AddressCode的长度; 5. 定义一个函数指针, 如 static void (*callFlashPrg2Address)(uint32_t addr, uint32_t data) 6. 定义一个函数实现将Code 运行与 RAM如: void run_prgcode_onram(uint32_t addr, uint32_t data) { memcpy(g_code,s_flashProg2AddressCode,32 ); callFlashPrg2Address = (void (*)(uint32_t addr, uint32_t data))((uin32_t)g_code + 1); callFlashPrg2Address (address, data); } run_prgcode_onram, 便可以将program_word2addr 运行于RAM中。 callFlashPrg2Address = (void (*)(uint32_t addr, uint32_t data))((uin32_t)g_code + 1); +1 的目的,时由于运行平台为 Cortex V6M , 采用的thumb指令集,根据ARM Spec 要 求完成。 callFlashPrg2Address (address, data); 则是实现RAM运行program_word2addr 的关键所在。

  • 发表了主题帖: 关于SPI-Flash的一些基础知识

    1.不同的SPIFLASH芯片可能会提供的擦除方式:扇区擦除(4KBytes),半块擦除(32KBytes),块擦除(64KBytes),片擦除。 2.不同的SPIFLASH芯片可能会提供的编程方式(也就是写数据):页编程(256Bytes),扇区编程(4KBytes)。 3.SPIFLASH如果擦除过,在往里面写0xFF这样的数据意义不大,因为它的特性就是擦除后数据就是0xFF。 4.写入flash时,只能把数据(bit)从1该为0。 5.传统的EEPROM的特点就是可以随机访问和修改任何一个字节,可以往每个bit中写入0或1。而写入flash时,只能把数据(bit)从1该为0。但是传统的EEPROM容量因成本的缘故收到限制,绝少有超过有512K的。 6.Nor Flash容量相对小,成本高,基本没坏块,数据线和地址线分开,可以实现随机寻址,读取任何一个字节,擦除任然要按块来擦。NAND FLASH容量大,成本低,坏块经常出现,但可以标记坏块,使软件跳过,数据线和地址线复用,按块擦除按页读取。

  • 发表了主题帖: liunx 配置静态IP 把ens33网卡改成eth0

    【】修改网卡将ens33改为eth0 1.编辑gurb配置文件 vim /etc/sysconfig/grub 修改GRUB_CMDLINE_LINUX变量增加两个参数 GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=cl/root rd.lvm.lv=cl/swap net.ifnames=0 biosdevname=0 rhgb quiet" 2、重新生成 grub 配置文件 grub2-mkconfig -o /boot/grub2/grub.cfg 然后重新启动 Linux 操作系统,通过 ip addr 可以看到网卡名称已经变为 eth0 。 3、修改网卡配置文件 mv /etc/sysconfig/network-scripts/ifcfg-ens33 /etc/sysconfig/network-scripts/ifcfg-eth0 # 修改ifcfg-eth0文件如下内容(其它内容不变) NAME=eth0 DEVICE=eth0 [root@localhost ~]# systemctl restart network.service    # 重启网络服务service network restart  【】用NET网络连接方式配置静态ip 1.vi /etc/sysconfig/network-scripts/ifcfg-eth0 2.vim /etc/udev/rules.d/70-persistent-ipoib.rules  3.修改主机名称 vim /etc/sysconfig/network 修改主机名称 NETWORKING=yes HOSTNAME=[你的主机名]

最近访客

< 1/5 >

统计信息

已有98人来访过

  • 芯币:1686
  • 好友:1
  • 主题:565
  • 回复:28
  • 课时:--
  • 资源:--

留言

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


现在还没有留言