灞波儿奔

  • 2019-06-14
  • 发表了主题帖: DM6437向DSP/BIO中增加硬中断的方法

    64+的中断系统和以往的不同,中断是基于事件的。整个硬件CPU接收15个中断,但中断源可以支持最多128个。64+将中断源视为事件"Event",128个事件可以分别通过配置连接到15个CPU中断。而128个事件每连续32个可以合并到四个固定的事件中,即Event0(对应事件号0-31)、Event1(对应事件号32-63)、Event2(对应事件号64-95)、Event3(对应事件号96-127)(实际最大有效中断源为124个)。这样可以通过数量有限的CPU中断来管理大量的中断源,使用灵活。      DSP/BIOS默认将Event0-3分别对应到HWI_INT7-10四个中断号。TI驱动通过注册Event到这四个事件,进而对应到相应中断。在中断HWI_INT7-10服务函数中再去判断具体是哪个事件触发的中断。      另外还有几个中断是系统硬件复位中断、NMI中断、预保留中断及仿真通讯中断。HWI_INT4-6、HWI_INT13、HWI_INT15这5个中断是未使用中断。用户增加中断服务的话,可以将事件号对应到这5个中断来实现。      以T1高32位定时中断为例,介绍一下DM6437应用中如何在DSP/BIOS操作系统中实现硬件中断。首先用户需要将T1配置为双32位定时器,T1高32位定时器中断服务函数为 T1HIsr()。将事件对应到中断HWI_INT5。      1 在DSP/BIOS图形配置界面下打开"Scheduling"->"HWI-Hardware Interrupu Service"。      2 下拉表中有15个中断。观察各中断使用情况。右键点击"HWI_INT5",弹出下拉菜单中选"Properties"进入中断属性编辑栏。在"General"栏中,"interrpu selection number"项填入事件号"7"(6437数据手册中有中断源对应的事件号)。"function"项填入中断服务函数名"_T1HIsr"(需在C语言编写的函数前加下划线)。然后点"应用","确定"即可退出。      注:一些人说还要在"Dispatcher"中勾选"Use Dispatcher"前的复选框。那是复合事件Event0-3才需要的,所以HWI_INT7-10四个中断里的"Use Dispatcher"需要勾上。如果用户定义的新中断不是使用复合事件而是使用单一事件触发的话不需要勾选。但勾选的话不会影响使用。因为新中断未使用Event0-3。      3 在应用程序中使能中断。如"C64_enableIER( 1 << 5 )" 使能中断HWI_INT5。      4 编写中断服务函数"void T1HIsr()"。      注。仿真器通讯中断可能对新中断有影响,有可能导致中断表现不正常。可以在"release"下编译,以减少仿真引起的大量中断。

  • 发表了主题帖: TxCON最高位导致的2812 TxPR寄存器写失败

    CCS仿真环境中,2812的TxPR寄存器的值更新会受TxCON里最高位(free位,为0表示仿真挂起会使计时停止,为1则计时不受挂起影响)影响。而TxCMPR里的值不受该位影响。如下面的程序代码:        EvaRegs.T2PR = 0x0080;        EvaRegs.T2CMPR = 0x0040;        EvaRegs.T2CON.all = 0x1546;    // Free = 0        EvaRegs.T2PR = 0x0090;        EvaRegs.T2CMPR = 0x0x0050;  执行后T2PR = 0x0080;       T2CMPR = 0x0x0050; 将TxCON里free位改为1:        EvaRegs.T2PR = 0x0080;        EvaRegs.T2CMPR = 0x0040;        EvaRegs.T2CON.all = 0x9546;    // Free = 1        EvaRegs.T2PR = 0x0090;        EvaRegs.T2CMPR = 0x0x0050; 执行后T2PR = 0x0090;       T2CMPR = 0x0x0050; 寄存器, 程序, 影响

  • 发表了主题帖: CCS下28XXDSP代码编译问题

    DelayMs(Uint16 t)是利用2812定时器进行延时的函数,定时中断每1ms对全局变量timedelay进行减1处理(为0则不减)。当timedelay由t变为0则跳出函数返回。但程序总是停到这个循环里无法跳出。     原函数为: void DelayMs(Uint16 t) { timedelay = t; while (timedelay != 0) { } }     下面是编译结果:     因为timedelay在中断中更改后AL会随中断的返回而进行出栈,所以在比较判断的时候AL的值是不变的,因此函数会陷入死循环。     下图看到timedelay对应的RAM中数据已经为0。     利用 for(timedelay = t; timedelay > 0;){} 也是相同的编译结果。甚至在while(1)中嵌入if (timedelay == 0) ... 也是相同的编译结果。对AL的值不重加裁。     这应当是CCS3.3中28XX DSP编译器的问题,在使用的时候需注意。     可以在while语句的判断条件中加一个额外无用的条件来强迫AL进行一次操作,这样再判断timedelay的时候就会对AL进行重新加载。     如改为 void DelayMs(Uint16 t) { timedelay = t; while (dmtimeren.bit15 == 1 && timedelay != 0) { } }     编译结果如下:     可见每次比较前都通过MOV AL,@10对AL进行了更新,当timedelay降到0后可以正常跳出。       

  • 发表了主题帖: DSP外扩SRAM的应用测试心得体会

    DSP的硬件测试说明: 平台说明: 图1-1  Code Composer Studio 6.0.0.00190 C/C++开发环境 硬件平台基于TMS320F28335芯片,软件平台基于TI的Code Composer Studio 6.0.0.00190 C/C++开发环境,如图1-1所示。 工作进度说明:   1.完成了DSP外扩SRAM的硬件的完整测试,通过修改TI官方的.cmd文件和库函数调用, 可以将FLASH中的函数复制到外部SRAM中运行,大大提高DSP程序的运行速度,附有详细说明。 2. 完成了RS485的驱动编写及应用测试。 3.利用官方的ADC_DMA、I2C例程,完成ADC和EEPROM的测试,整合了ADC_DMA例程。 4.CAN通讯和转速中断的代码公司已有,未整理。 5.DSP内置的看门狗模块已开启。 一、DSP扩展的外部SRAM应用说明(已测试完毕) 前言: DSP可以工作中150MHz的频率下,为了发挥其高速运行的特性, 一般会将 FLASH程序内容复制到外部高速SRAM中运行,TMS320F28335内部有34K X 16bit的SRAM, TI将内部SRAM分成了多块(见F28335.cmd)。 一般的程序在内置FLASH中运行,程序在FLASH中的运行速度由FLASH的读取速度决定, 如果没有采用FLASH的加速技术,一般需要设置等待时间。 要想使程序高速运转, 最少要扩展一块SRAM 来高速运行DSP算法或中断函数。 1.外部SRAM分区说明 本项目的硬件扩展了256K 16bit SRAM 时钟延迟为10ns SRAM型号为: IS61LV25616AL-10T 定位地址为: 0x0200000 前128K 用作程序空间,后128K用作数据空间 定位地址和ARM的内部SRAM地址一样,DSP中文数据手册参考内容如下图所示(可放大):   图1-2  DSP典型的16位和32位数据总线连接示意图 IS61LV25616AL-10T芯片的数据总线是16位,后面的STM32F429板子也用了这个芯片,但增加了高低位选通线, 因此可以读高低字节,比DSP更灵活:DSP最少一次读2字节,ARM想读任何字节都可以。另外ARM内部有FLASH加速, 可以直接跑180MHz,外设是90MHz,但比DSP性能相差已经比较小了。 DSP的GPIO37/XZCS7引脚控制的外部SRAM的物理地址如下图所示:  图1-3 GPIO37/XZCS7引脚控制的外部SRAM的物理地址说明 在F28335.CMD文件分配,详细内容如下: XINTF zone 7 - program space ZONE7A : origin = 0x0200000, length = 0x020000 XINTF zone 7 - data space ZONE7B : origin = 0x0220000, length = 0x020000 2.外部DATA SRAM使用说明 内部SRAM不够用,则使用外部DATA SRAM,用法如下 #pragma DATA_SECTION(bufferB, "ZONE7DATA"); uint16_t bufferB[512]; 仿真测试结果: 观察数据0x22000区域,可以看到bufferB区域被程序设置了正确的数据。 2.外部CODE SRAM使用说明 外部中断函数或一般函数使用如下格式的声明 #pragma CODE_SECTION(cpu_timer0_isr,"xintffuncs"); #pragma CODE_SECTION(cpu_timer1_isr,"xintffuncs"); 编译器会将这些特定的函数分配在一个固定的FLASH区域 起始地址为XintffuncsLoadStart 结束地址为XintffuncsLoadEnd 主程序中XINTF Zone 7初始化后,调用如下函数将这个区域的特定函数 复制到外部SRAM的运行程序空间(0x0200000-0x021FFFF) MemCopy(&XintffuncsLoadStart, &XintffuncsLoadEnd, &XintffuncsRunStart); 频繁运行或调用的捕获函数、定时器函数或特定算法,只有在高速SRAM中运行, 才能真正发挥TMS320C28335的实际性能。 仿真测试结果: cpu_timer0_isr和cpu_timer1_isr定位在0x200000-0x21FFFF区域 对这两个中断函数设置断点,可以正常进入中断运行。 定时器0中断函数的实际仿真结果如下图所示: 图1-4  定时器0中断函数的加载到外部SRAM前后对比分析图 定时器1中断函数的实际断点仿真如下(是无损压缩图片,可以放大): 图1-5  定时器1中断代码的运行地址分析 可以得出结论,在反汇编窗口,看到定时器1中断的入口地址是0x2002a,在1ms的定时器1中断内翻转GPIO50脚,产生一个方波。 图1-6  定时器1中断的应用测试截图 该图比较小,是因为用的是小示波器保存到U盘的图片。用该示波器保存成csv格式,仅有2500个数据。 录波仪录制数据波形比较长,十几秒就可以存下超过100万个点的数据,当然普通的.xls或.xlsx文件是存不下的。 二、通信配置说明(进行中) 1. RS485通信配置和应用测试代码      以下是网络参考代码,实际的配置比较繁琐,这里简略不写了。      // 重新映射 PIE -  Timer 0的中断         EALLOW;  // 解除寄存器保护     PieVectTable.TXBINT = &SCI_TX_isr;     PieVectTable.RXBINT = &SCI_RX_isr;     EDIS;    // 使能寄存器保护         // 使能接收中断     PieCtrlRegs.PIEIER8.bit.INTx3 = 1;     // 使能发送中断     // PieCtrlRegs.PIEIER9.bit.INTx4 = 1;       IER |= 0x100;           // 全局中断使能和更高优先级的实时调试事件     EINT;   // 全局中断使能INTM     ERTM;   // 使能实时调试中断DBGM SCIC串口发送和接收的大致流程如图1-6所示。 图1-7  SCIC串口的发送和接收流程     // 利用TMS320F28335的SCIC串口FIFO来发送数据,只要数据包不超过16,    // 都可以无需等待,直接将所有字节放入FIFO,极大的节约CPU的时间开销    // 具体实现如下,至于官方的例程,等待当前字节发送完毕再发下一个,    // 这样太浪费时间,在实际产品中基本上没人用    if(Flag_CpuTimer0){          Flag_CpuTimer0 = 0; // 10ms 定时周期          GpioDataRegs.GPBSET.bit.GPIO50 = 1;   // RS485 DE = 1 RS485切换为发送状态          for(i = 0; i < 10; ++i){              ScicRegs.SCITXBUF = bufferA; // 将10个数据放入FIFO,然后自动依次转移到移位寄存器          }          asm(" nop");          asm(" nop");          asm(" nop");          asm(" nop");          asm(" nop");          asm(" nop");      }      // RS485处于发送状态,且SCIC移位寄存器为空     if((GpioDataRegs.GPBDAT.bit.GPIO50 == 1)&&(ScicRegs.SCICTL2.bit.TXEMPTY == 1)){           GpioDataRegs.GPBCLEAR.bit.GPIO50 = 1;   // RS485 DE = 0 RS485转为接收状态     } 2. CAN通信配置    进行中 三、数据存储器配置说明(I2C)    扩展了512K byte EEPROM,打开官方的I2C例程(非GPIO模拟的时序),已测试OK。 四、集成功率芯片测试(GPIO)    主要是对功率器件的测试,进行中 五、看门狗配置说明(已完成) 一般嵌入式系统都需要开启外部或内部看门狗,以防意外的CPU挂起死机 六、TI官方的CMD文件说明(实际应用的文件是在官方的基础上稍作修改) TI官方提供的CMD文件有三个: 28335_RAM_lnk.cmd、F28335.cmd、DSP2833x_Headers_nonBIOS.cmd 调试程序时,将28335_RAM_lnk.cmd文件加入工程中,程序被加载到SRAM中运行。 实际的产品运行时,需要将程序下载到FLASH中,并从FLASH中启动,这时需要 在工程中加入F28335.cmd文件,同时屏蔽28335_RAM_lnk.cmd文件。 TI官方文件F28335.cmd对内部SRAM的分配如下: BOOT_RSVD : origin = 0x000000, length = 0x000050 // Part of M0, BOOT rom will use this for stack PAGE 1 RAMM0 : origin = 0x000050, length = 0x0003B0 // on-chip RAM block M0 PAGE 1 RAMM1 : origin = 0x000400, length = 0x000400 // on-chip RAM block M1 PAGE 1 ZONE0 : origin = 0x004000, length = 0x001000 // XINTF zone 0 PAGE 0 RAML0 : origin = 0x008000, length = 0x001000 // on-chip RAM block L0 PAGE 0 RAML1 : origin = 0x009000, length = 0x001000 // on-chip RAM block L1 PAGE 0 RAML2 : origin = 0x00A000, length = 0x001000 // on-chip RAM block L2 PAGE 0 RAML3 : origin = 0x00B000, length = 0x001000 // on-chip RAM block L3 PAGE 0 RAML4 : origin = 0x00C000, length = 0x001000 // on-chip RAM block L1 PAGE 1 RAML5 : origin = 0x00D000, length = 0x001000 // on-chip RAM block L1 PAGE 1 RAML6 : origin = 0x00E000, length = 0x001000 // on-chip RAM block L1 PAGE 1 RAML7 : origin = 0x00F000, length = 0x001000 // on-chip RAM block L1 PAGE 1 Allocate DMA-accessible RAM sections: DMARAML4 : > RAML4, PAGE = 1 DMARAML5 : > RAML5, PAGE = 1 DMARAML6 : > RAML6, PAGE = 1 DMARAML7 : > RAML7, PAGE = 1 Allocate uninitalized data sections: .stack : > RAMM1 PAGE = 1 .ebss : > RAML4 PAGE = 1 .esysmem : > RAMM1 PAGE = 1 TI官方文件DSP2833x_Headers_nonBIOS.cmd对特殊功能寄存器的映射(PAGE 1)如下 DEV_EMU : origin = 0x000880, length = 0x000180 // device emulation registers FLASH_REGS : origin = 0x000A80, length = 0x000060 // FLASH registers CSM : origin = 0x000AE0, length = 0x000010 // code security module registers ADC_MIRROR : origin = 0x000B00, length = 0x000010 // ADC Results register mirror XINTF : origin = 0x000B20, length = 0x000020 // external interface registers CPU_TIMER0 : origin = 0x000C00, length = 0x000008 // CPU Timer0 registers CPU_TIMER1 : origin = 0x000C08, length = 0x000008 // CPU Timer0 registers (CPU Timer1 & Timer2 reserved TI use) CPU_TIMER2 : origin = 0x000C10, length = 0x000008 // CPU Timer0 registers (CPU Timer1 & Timer2 reserved TI use) PIE_CTRL : origin = 0x000CE0, length = 0x000020 // PIE control registers PIE_VECT : origin = 0x000D00, length = 0x000100 // PIE Vector Table DMA : origin = 0x001000, length = 0x000200 // DMA registers MCBSPA : origin = 0x005000, length = 0x000040 // McBSP-A registers MCBSPB : origin = 0x005040, length = 0x000040 // McBSP-B registers ECANA : origin = 0x006000, length = 0x000040 // eCAN-A control and status registers ECANA_LAM : origin = 0x006040, length = 0x000040 // eCAN-A local acceptance masks ECANA_MOTS : origin = 0x006080, length = 0x000040 // eCAN-A message object time stamps ECANA_MOTO : origin = 0x0060C0, length = 0x000040 // eCAN-A object time-out registers ECANA_MBOX : origin = 0x006100, length = 0x000100 // eCAN-A mailboxes ECANB : origin = 0x006200, length = 0x000040 // eCAN-B control and status registers ECANB_LAM : origin = 0x006240, length = 0x000040 // eCAN-B local acceptance masks ECANB_MOTS : origin = 0x006280, length = 0x000040 // eCAN-B message object time stamps ECANB_MOTO : origin = 0x0062C0, length = 0x000040 // eCAN-B object time-out registers ECANB_MBOX : origin = 0x006300, length = 0x000100 // eCAN-B mailboxes EPWM1 : origin = 0x006800, length = 0x000022 // Enhanced PWM 1 registers EPWM2 : origin = 0x006840, length = 0x000022 // Enhanced PWM 2 registers EPWM3 : origin = 0x006880, length = 0x000022 // Enhanced PWM 3 registers EPWM4 : origin = 0x0068C0, length = 0x000022 // Enhanced PWM 4 registers EPWM5 : origin = 0x006900, length = 0x000022 // Enhanced PWM 5 registers EPWM6 : origin = 0x006940, length = 0x000022 // Enhanced PWM 6 registers ECAP1 : origin = 0x006A00, length = 0x000020 // Enhanced Capture 1 registers ECAP2 : origin = 0x006A20, length = 0x000020 // Enhanced Capture 2 registers ECAP3 : origin = 0x006A40, length = 0x000020 // Enhanced Capture 3 registers ECAP4 : origin = 0x006A60, length = 0x000020 // Enhanced Capture 4 registers ECAP5 : origin = 0x006A80, length = 0x000020 // Enhanced Capture 5 registers ECAP6 : origin = 0x006AA0, length = 0x000020 // Enhanced Capture 6 registers EQEP1 : origin = 0x006B00, length = 0x000040 // Enhanced QEP 1 registers EQEP2 : origin = 0x006B40, length = 0x000040 // Enhanced QEP 2 registers GPIOCTRL : origin = 0x006F80, length = 0x000040 // GPIO control registers GPIODAT : origin = 0x006FC0, length = 0x000020 // GPIO data registers GPIOINT : origin = 0x006FE0, length = 0x000020 // GPIO interrupt/LPM registers SYSTEM : origin = 0x007010, length = 0x000020 // System control registers SPIA : origin = 0x007040, length = 0x000010 // SPI-A registers SCIA : origin = 0x007050, length = 0x000010 // SCI-A registers XINTRUPT : origin = 0x007070, length = 0x000010 // external interrupt registers ADC : origin = 0x007100, length = 0x000020 // ADC registers SCIB : origin = 0x007750, length = 0x000010 // SCI-B registers SCIC : origin = 0x007770, length = 0x000010 // SCI-C registers I2CA : origin = 0x007900, length = 0x000040 // I2C-A registers CSM_PWL : origin = 0x33FFF8, length = 0x000008 // Part of FLASHA. CSM password locations PARTID : origin = 0x380090, length = 0x000001 // Part ID register location

  • 发表了主题帖: DSP C语言基础要点

         直到接触到DSP,由于使用TI的库文件例程的缘故,对结构体,联合体等有了进一步的了解,也对一个工程变量的使用有了更深的认识。下面对谈谈对DSP C刚入门者的一些建议:    1.DSP程序的定位配置---CMD文件,要求熟悉DSP的存储器结构。这一部分对接触硬件多一些的新手很容易,其实也就是配置一般的C语言编译器的一部分,比如AVR单片机的头文件,cpu选择等在DSP中就是这一部分完成。另外还要注意TI例程中对加密位,看门狗的处理。    2.当然是C语言的一些基础了:运算符,优先级,几个基本流程控制语句。这些无论是什么C平台运行都一样的,基础中的基础。但是也很容易因为这些基础的简单的问题,导致一些很郁闷的结果。也是因为对这些基础的概念的理解深入程度影响你的软件思想。    3.结构体,联合体,位域的运用,最好能有一个对比,了解运用的场合及优缺点。有了这些基础,就很容易明白TI例程的一些基本结构。    4.变量的作用域以及生存期,这样才能从最初的单个的C文件过渡到C工程,才能更好地了解模块化编程地基本要素,实现基础。    5.Q格式等的应用,因为大多数DSP都是定点的,而做运算处理的很多时候都不可避免要用到浮点数。使用Q格式才能更好的发挥dsp高速的特性。所以有人说“不会使用Q格式的DSP程序员不是优秀的程序员”!    6.另外我觉得如果用DSP参与控制的话,最好能熟悉一些面向对象的程序语言,比如C++。要能了解用C实现面向对象的方法。为什么呢?一是面向对象我个人觉得在写程序方面模块化等要好一些,特别是控制领域,我看过一些高手的PLC程序也是采用的面向对象的思想构件模块的,从那以后我的单片机程序才开始这样靠拢,也确实发现了一些好处。比如对那些c流程中多次要操作的资源,有时候会出现一些共用操作的位置错误。二是TI的例程中有好多地方用到了这些思想,要明白这些东西,你才能更容易理解那些例程。这一部分我推荐一本书《DSP C2000程序员高手进阶》。   7.数据结构知识,DSP要做运算,肯定要涉及到一些好的数据结构。才能更好的优化算法。   8.matlab的运用。matlab提供了强大的数学运算能力,还能对DSP结合CCS进行仿真。可以把程序员从繁琐的底层编程中解放出来。个人感觉matlab给编程思想和底层程序提供了一个连接,一个平台。

  • 发表了主题帖: RF5架构简介

          RF5是德州仪器TI公司DSP软件开发的起步代码参考框架,它以DSP/BIOS为基础,利用其中的数据处理元素和数据通信元素方便快捷地完成DSP软件的设计与开发。RF5是RF的最新版本,其区别于RF1和RF3的显著特点是其支持动态对象创建和支持线程(任务)挂起功能,因此适合系统较复杂的应用场合。 RF5 主要实现三个功能,存储管理,线程模型和通道封装,对于不同的应用,我们只需在这三个元素上做修改,而对于整个应用程序,不用从头设计,这样大大简化了开发者的开发难度,缩短了开发时间。       RF5适用于包含大量的算法,且要求多线程,多通道的应用,如图像处理,多媒体应用等,以Ti提供的实例mpeg2loopback为例,对RF5进行分析。       RF5包含的元素有: 1 线程(Thread):       RF5框架包含四个基本的数据处理元素,处在最顶层的是线程,线程总是顺序的执行所包含的通道,线程在一个比较高级的级别上把数据组织在一起,他们可以与别的线程,设备驱动以及别的类似结构进行通讯,在mpeg2lookback实例中,创建了三个线程分别是tskVideoInput, tskVideoOutput和tskProcess。每个线程都在不断的等待消息,处理数据,并将结果发送给其他的线程,同时有可能还要发送同步消息给其他线程以实现线程间的通讯,这里使用的机制是SCOM模块。       每个线程都是进行数据处理的一个单元,有的处理是简单的,有的处理是相对复杂的过程,简单的线程可以不包括任何的通道,而进行复杂数据处理的线程有可能包含多个的通道。 2 通道(Channel):       RF5提供了一种通道结构是为了更方便的封装算法,这可以理解通道为并行里的串行,因为线程的执行就是由通道的串行执行来完成的,一个通道包含一组核(Icell)。其主要任务就是依次顺序的执行所包含的核,主要执行的流程为:首先需要初始化通道模块,然后建立通道对象,注册该通道所包含的核对象,接着依次执行每个核,执行完成了后就销毁对象,最后退出。每个通道可以包含多个核,每个核都要进行初始化后再调用CHAN_regCell注册。 通道对象的结构如下: typedef struct CHAN_Obj {      ICELL_Obj *cellSet;          /* set of cells in the channel */      Uns cellCnt;                 /* number of cells in the cellSet */      CHAN_State state;            /* state of the channel */      Bool (*chanControlCB)(CHAN_Handle chanHandle); /* optional control function */ } CHAN_Obj;       线程一般不定义通道对象,但是在CHAN_open()调用中初始化它们。CHAN_open()的最后一个参数是通道属性(CHAN_Attrs)结构体的地址。如果最后一个参数是NULL,那么CHAN_open()使用默认的参数。如果要想使用不同的参数,就要声明一个CHAN_Attrs的结构体,并需初始化为CHAN_ATTRS宏所定义的初值,然后根据需要可以修改其中相应的域的值。通常,其中的通道状态参数CHAN_State state域默认为CHAN_ACTIVE,通道控制回调函数参数域Bool (*chanControlCB)(CHAN_Handle chanHandle)默认为NULL。如果通道控制回调函数不是空,那么在任何的cell调用执行之前都会先调用此回调函数。      一个典型的设置:一个线程为每一个通道建立一个CHAN_Obj对象(或者一组类似的对象),并且为每一个cell建立一个ICELL_Obj对象(或者是与每个通道相对应的一组ICCE_Obj对象)。在线程初始化ICELL_Obj之后就会调用下面的函数: 备注其中的cell 指向cell 对象的指针, inputIcc/outputIcc是相应的cell的 ICC 对象,这个调用计算单元需要的空间,并分配给定的ICC对象给单元cell。    CHAN_regCell( cell, inputIcc, 1, outputIcc, 1 );    当所有的cells都已经创建并初始化之后,线程调用CHAN_open()函数来为每一个指定的通道(chanNum)传递cell对象(cellList)。这个函数创建所有的XDAIS算法,并且如果单元细胞定义了cellOpen函数,则会调用每一个单元细胞的cellOpen函数,.    CHAN_open( chanList[ chanNum ], cellList, numCells, NULL/* default attributes */ ); 最后,在运行时,线程为每一个通道(chanNum)调用CHAN_execute函数开始执行:    CHAN_execute( chanList[ chanNum ], NULL /* arg to cells */ ); 3 核(Icell):       核实际上就是ICELL接口对象,基于RF5的应用常常包含大量的算法和通道。为了便于算法集中到应用中,RF5提出了核的概念。一个核就是包含一种 XDAIS算法的容器,一个RF5通道对象可以包含多个核,也即是包含多个算法。通道通过核来调用算法,实际上,真正的数据处理是在XDAIS算法中进行的,核只是提供一个调用算法的接口,这大大简化了工作量,便于移植。       该接口包含一个重要的结构:ICELL_Fxns,该结构包含一组函数指针。通道通过调用这些函数来调用算法,其中包含一个关键的函数 cellExecute,这个函数的功能是调用XDAIS算法来执行,上面的通道执行函数CHAN_execute就包含了每个cellExecute的调用。 4 ICC模块       ICC模块是用来管理在核之间以及核与其他线程之间的数据通讯,我们知道线程间的数据传输是通过SCOM模块来实现的,每个ICC模块管理一个或者多个 ICC对象,每个核都有一组输入和输出ICC对象。这些对象是通过CHAN_regCell()来注册到相应的通道里。 5 同步通讯机制(SCOM)       ThrProcess中包含两个SCOM对象,RF5使用SCOM对象来实现线程间的通讯。SCOM消息是用户自定义的一个机构,一个线程通过调用 SCOM_putMsg()函数将SCOM消息放置到一个SCOM队列中,发送给其他的线程,或者通过调用SCOM_getMsg()函数从队列中获取消息。一般情况下,发送消息指明接收线程所要读取的数据缓冲区的地址(以指针形式),接收消息指明发送线程所要写入的数据缓冲区的地址。在 mape2loopback实例中,thrProcess要从thraVideoInput接收消息,并发送消息给thrVideoOutput输出图像。RF5使用SCOM来实现线程间的通讯:thrProcess拥有一些缓冲区,需要thrVideoInput写或thrVideoOutput读,所以thrProcess通过SCOM告诉thrVideoIput和thrVideoOutput线程数据缓冲区的地址,同时还要保证两个线程不会同时访问同一个缓冲区。thrProcess创建了两种消息以分别和两个线程进行通讯,scomMsgRx和scomMsgTx。scomMsgRx指定了被 thrVideoInput写的缓冲区地址,scomMsgTx指定了被thrVideoOutput读的缓冲区地址。       在实际的操作中,可以将SCOM看作是一种同步标记,它用来区分模块内存是否正在被其他线程所使用,这样就可以防止内存访问的冲突。整个系统中包含很多存储区,这些存储区很有可能在某一时刻正在被某一线程访问,为了保证在任意时刻只有一个线程访问某该存储区,当前正在访问这一内存块的线程通过发送SCOM消息给与这一内存块有关联的线程,告诉它们,“我正在访问呢,你等会再来吧”。当它访问完后,放弃了这一内存块的占有权,再通过SCOM消息告诉相关联的线程,“我用完了,你可以用了。”于是相关联的线程就可以访问了。 6 ALGRF模块—算法的实例化Algorithm Instantiation ALGRF Module用DSP/BIOSMEM内存管理器来创建和删除XDAIS算法的模块。参考框架服务简化XDAIS部件的使用。所有符合XDAIS标准的算法都必须使用一个标准的接口——IALG接口。ALGRF使用算法的IALG来实现对XDAIS算法的实例化。任何符合XDAIS标准的算法都可以被 ALGRF所使用。 用户代码不必直接的调用ALGRF函数,这个工作由CHAN和其他的库函数来完成。例外的是cell wrappers中的ALGRF_activate/deactivate序列化:如果cell中的XDAIS算法执行 IALG_active/deactivate函数,细胞需要调用两个ALGRF函数来完成。  三个模块来简化IALG接口创建算法对象:RF5使用ALGRF模块来创建,配置,删除XDAIS算法实例;ALG模块使用CCStudio作为通用目的使用;并且不用DSP/BIOS MEM模块分配内存。ALGMIN是三个中的最小应用。 一般情况下,三个模块是相互包含的,但是只有一个能在应用程序中使用。ALGRF适于RF5的需要和别的RF级别,而不适合紧凑和底端的如RF1级别的系统。 ALGRF与ALG相比,有以下的优势: 1 更小的代码脚本(代码量): 作为一个通用的模块,ALG支持malloc/free 运行库和DSP/BIOS MEM_alloc/MEM_free动态内存分配方式,ALGRF只支持DSP/BIOS分配,这为设计者省了代码空间,另外ALGRF保证没有无用代码的存在。只有被调用的函数才被连接到执行程序中。 2 暂存区支持:   下面为API在ALGRF中介绍的实例: ALGRF_Handle ALGRF_createScratchSupport(IALG_Fxns *fxns, IALG_Handle parent, IALG_Params *params, Void *scratchBuf, Uns scratchSize) 除了当IALG_SCRATCH内存区域(内部数据缓冲区)被请求,该函数能根据算法请求分配内存。作为替代,scratchBuf和scratchSize这两个参数表明这一缓冲区已经在应用中存在,而且可以被当前的算法重新使用。这就可以受约束的共享资源有限的内存区。 3 从DSP/BIOS堆标签中做提取:   ALGRF使用DSP/BIOS 的MEM模块动态分配内存。一个堆标记或内存段名可以传给 MEM_alloc()来表明分配到哪一个堆.如下:      /* Configure the ALGRF module to use:      * 1st argument - memory for internal heap      * 2nd argument - memory for external heap      */   ALGRF_setup( INTERNALHEAP, EXTERNALHEAP );   这让我们可以指定算法的数据分配位置。例如,如果使用EXTERNALHEAP作为两个参数,那么算法的数据就被定位在外部存储器。

  • 2019-06-12
  • 发表了主题帖: TMS320F2812实现SVPWM的完整程序

    //########################################################################### // // FILE:        DSP281x_Ev.c // // TITLE:        DSP281x Event Manager Initialization & Support Functions. // //########################################################################### // //  Ver | dd mmm yyyy | Who  | Description of changes // =====|=============|======|=============================================== //  1.00| 11 Sep 2003 | L.H. | No change since previous version (v.58 Alpha) //########################################################################### #include "DSP281x_Device.h"     // DSP281x Headerfile Include File #include "DSP281x_Examples.h"   // DSP281x Examples Include File //--------------------------------------------------------------------------- // InitEv:  //--------------------------------------------------------------------------- // This function initializes to a known state. // void InitEv(void) {     EALLOW;    // Initalize EVA Timer1&Configure PWM1-PWM6         //EvaRegs.T1PR = 625;            // period=625×2/25M ,PWM=20KHz                 EvaRegs.T1PR = 781;            // period=781×2/25M ,PWM=16KHz                 EvaRegs.T1CNT = 0;              // Timer1 counter         //定时器控制寄存器A                 EvaRegs.GPTCONA.bit.TCMPOE=1;   // enable timer compare                 EvaRegs.GPTCONA.bit.T1PIN=0;    // timer compare output         //计时器比较寄存器     EvaRegs.T1CMPR=0;                 EvaRegs.EVAIMRA.bit.T1UFINT = 1;// enable Timer1 period interrupt                                                                         //EVA中断屏蔽寄存器                 EvaRegs.EVAIFRA.bit.T1UFINT=1;  // clear Timer1 period interrupt flag                                                                         //EVA中断标志寄存器 /****************************************************   TMODE = continuous up/down count   Input clock prescaler= X/1 (X=HSPCLK)=25MHz   Timer enable ; Timer compare enable *****************************************************/                 EvaRegs.T1CON.all = 0x0802;     //连续增减计数,X/1        定时器尚未打开             //Enable compare for PWM1-PWM6                 EvaRegs.CMPR1 = 781;                 EvaRegs.CMPR2 = 781;                 EvaRegs.CMPR3 = 781;                 EvaRegs.ACTRA.all = 0x0999;   // PWM1,2,3,4,5,6                 EvaRegs.DBTCONA.all = 0x0AEC; // 死区2.88us  9/(25/8)                  EvaRegs.COMCONA.all = 0xa600; //使能比较操作,不使能全局比较输出 //******************  QEP初始化   ********************        // EvbRegs.T4CON.all=0x1870;         //        EvbRegs.T4CNT=0x0000;         //        EvbRegs.T4PR=0xffff;                 EvaRegs.T2CON.all=0x1870;                 EvaRegs.T2CNT=0x0000;                 EvaRegs.T2PR=0xffff;                 EvaRegs.T1CON.bit.TENABLE=0; //****************************************************                 EDIS; }                  //=========================================================================== // No more. //================  

  • 2019-06-11
  • 发表了主题帖: 如何进行产品的标准ZigBee测试认证

         以开发标准ZigBee Home Automation相关产品为例。首先开发者开发产品时要按照ZigBee Home Automation Profile Specification 中描述的产品进行开发,这个文档可以在www.zigbee.org下载到。       在完成产品的开发后,开发着需要了解ZigBee Home Automation Profile Test Specification, 这个文档描述了一个特定产品需要在Test House过的相关测试项,文档也可以在www.zigbee.org下载到,另外除了以上两个文档以外还有一个PICS文档,这个文档专门用于描述需要过认证测试产品所支持的功能,开发者根据开发产品的实际红能,和Specification中所要求的功能,在文档中进行打钩确认。 下面是测试的流程, 1) 首先加入ZigBee联盟,一般可以有测试实验室帮助完成。 2) 寄送样品到测试实验室,完成PICS文档的填写。 3) 第一轮预测试,测试实验室对测试结果反馈,开发者修改样品代码。 4) 测试实验室对修改后的样品进行验证,然后开始正式测试。 5) 测试实验室协助开发者完成ZigBee联盟网上认证申请资料的准备和提交。 6) 测试实验室提交正式测试报告给ZigBee联盟。联盟会完成审核并发证 目前国内可以完成标准ZigBee测试的测试实验室有两家 1) CESI 北京 中国标准化电子研究所。 2) Element 深圳办事处(总部在英国) 详细可以参考下面的wiki地址, http://processors.wiki.ti.com/index.php/ZigBee_Product_Certification_Guide

  • 发表了主题帖: TI的ZigBee协议栈不同版本的区别

         TI ZigBee 协议栈Z-Stack从最开始的Z-Stack 0.1到大家熟悉的Z-Stack 2.5.1a,以及到现在Z-Stack Home 1.2.1, Z-Stack Lghting 1.0.2, Z-Stack Energy 1.0.1, Z-Stack Mesh 1.0.0. 在协议栈的升级过程TI主要对协议栈做了两方面的工作,1) 根据ZigBee Alliance的ZigBee Specification进行一些新的Feature添加,比方说ZigBee2007是树形的路由,在ZigBee Pro中有了Mesh路由,并且提出了MTO和Source Routing等路由算法,所以TI的把相应新的功能添加到协议栈上去。当然有一部分是Spec中相关bug的修正,比方说有些描述模棱两可的;2) TI ZigBee协议栈本身软件bug的修复。一个版本的协议栈相对于之前一个版本协议栈的区别,都可以在协议栈安装目录下的Release Note中找到。      在Z-Stack 2.5.1a以后,TI的协议栈并没有继续以Z-Stack 2.6.x的形式直接发布,而是按照Application Profile的方式来发布了,原因在于TI希望开发者根据实际的应用选择更有针对的性的协议栈进行开发。像Z-Stack Home 1.2.1之类的协议栈,主要包括两部分,1)核心协议栈Core Stack,这部分起始就是之前的Z-Stack 2.5.1a以后的延续版本,可以在协议栈安装目录下 Z-Stack Core Release Notes.txt文件中找到,Version 2.6.2 。2)应用协议栈 Profile相关,这部分主要跟实际应用相关的,Home Automation 协议栈里都是ZigBee Home Automation Profile相关的实现。同样Z-Stack Lghting 1.0.2和Z-Stack Energy 1.0.1也是一个Core Stack再加上应用上的Profile。 1)Z-Stack Home 1.2.2a 针对智能家居相关产品的开发 2)Z-Stack Lighting 1.0.2 针对ZLL相关产品的开发 3)Z-Stack Energy 1.0.1 针对智能能源,Meter, In Home Display, 等相关产品的开发 4)Z-Stack Mesh 1.0.0 针对相关私有应用的产品的开发,只利用标准ZigBee协议相关功能, Mesh路由等,应用层有开发者自己定义。      在ZigBee联盟发布ZigBee 3.0协议以后,最新的ZigBee协议栈是Z-Stack 3.0, 目前支持的设备有CC2530, CC2538, CC2652R。

  • 2019-06-10
  • 发表了主题帖: MSP432学习心得之系统滴答定时器

        系统滴答定时器,在操作系统中是十分重要的,它可以提供一个好的系统时钟节拍,就和我们的心脏一样,跳动着一定的频率。它则为系统的运行提供了一个好的时间基准。这里呢,我们将使用它来完成一个延时函数的实现,为什么使用它,因为它跳动的很准确,而且配置相对简单,并且不会暂用我们的定时器或者其他外设。再也不用什么i呀,j呀,弄两个for循环在那里跑跑跑的延时,是不是很想尝试一下,我们来看。     MSP432的Cotex-M4的内核是有ARM提供的,所以这里很多东西都可以通用的,我们本次讨论的就是M3和M4中间都带有的一个系统滴答定时器。既然很多东西都一样,那么包括相关的寄存器的定义也都是一样的,所以我这次直接采用了原子的STM32中的延时函数代码,直接拷贝了过来,但是发现了其中一个问题,就是时钟的问题。找到我们432的数据手册中的滴答定时器的描述章节提到了。     那么这里的free running clock(FCLK)这里具体指的是什么呢?     ARM技术注:FCLK 为处理器的自由振荡的处理器时钟,用来采样中断和为调试模块计时。在处理器休眠时,通过FCLK 保证可以采样到中断和跟踪休眠事件。 Cortex-M3内核的“自由运行时钟(free running clock)”FCLK。“自由”表现在它不来自系统时钟HCLK,因此在系统时钟停止时FCLK 也继续运行。FCLK和HCLK 互相同步。FCLK 是一个自由振荡的HCLK。FCLK 和HCLK 应该互相平衡,保证进入Cortex-M3 时的延迟相同。     所以这里默认情况下,Systick的时钟是来自CPU的运行时钟的。     而这里的CPU使用的是MCLK。     那么我们所说的这个时钟到底对不对呢。等下我们进行验证。     那么我们现在可以暂时不管时钟是否准确,我们首先先假设为48MHz好了。 这里定义了两个变量,这两个变量为静态全局变量。解释一下为什么定义为静态全局变量。首先我们看下静态是什么意思,字面上看,可以理解该变量处于一种“安静的状态”,也就是时候它是个安静的美男子(或者美女子),那么有一个什么特点呢?就是懒得动。     举个简单的例子说明(这里使用了百度百科的程序)。     下面这个是静态的局部变量,而得到的实验结果。    而下面这段代码中就没有static关键字。得到的结果就发生了变化。      好了,现在我们来看一下加了static关键字的变量为什么“懒得动”了。我们可以看出fun()这个函数返回的是f = f * n这个式子的计算值。那么第一幅图中的代码是1,2,6,24,120的结果,我们分析一下。      在这个结果中,后面一个结果是由上面以一次计算而得到的f的值,再和后面的n相乘得到新的值,简而言之,当我们再次调用fun()这个函数的时候,是不执行static int f = 1;这个语句的,而下面那副图中没有static就会再次执行f = 1这个语句,所以每次都是1 * n才会得到1,2,3,4,5的结果。 所以呢,加了static之后会使得变量变懒,它会保持上次计算的值,除非进行重新赋值。相信大家通过上面那个代码可以比较清楚的理解到静态的意思。 那么对于静态局部变量和静态全局变量除了“懒”的特点之外,其他的都和我们的局部变量和局部变量一样。      那么我们这里为什么让两个倍乘数定义为静态的也就很好理解了,我一个寄存器拿来数数,我当然数字要么是一直累加或者减小,否则,我数一下,你就给我赋值成初始值,那我不是白数了,数来数去不还是原地踏步。所以这里定义为静态的。      但是这里如果心细一点会发现,其实这里静态和非静态其实效果是一样的,这里是因为我们定义为全局变量,那么在函数调用的时候不可能再次执行到这两个定义语句,也就是说他们执行的机会就有一次。所以这里如果你改成非静态的全局变量,同样是可以的。但是如果将这两个语句移到我们的函数中有时候需要考虑一下。这里保险起见我们定义成静态的。      上面我们假设了时钟为48MHz,所以这里暂且我们给fac_us赋值为48,这个应该好理解吧,48MHz的频率,数48下是1us,应该可以理解。不行的话自己算算哈。 fac_ms就毫无疑问了是fac_us的1000倍,这里就直接乘就ok了。     下面我们解释一下延时函数的实现,代码如下:      因为我是直接从STM32那边的代码直接拷贝过来的,原本以为说可以直接使用的,但是发现不行,编译通过了,但是在代码执行的时候回产生错误,所以就回过头来认真看432的数据手册。怎么说,数据手册就跟我们上学的教科书,而其他的东西就像我们的课外参考书一样,所以最主要的还是我们的教科书,一切都以教科书为准,除了问题当然首先回到我们的教科书,数据手册中找。     需要查看的的内容如下,红色的方框中标注。      在这些内容中我们找到了关于432的SysTick的使用说明,这里我们看到了配置的步骤说明,以及我们看到下面的注意事项,其中有一点很重要就是对于432的时钟源的控制位CLKSOURCE这个位必须置一,这一点很重要。      时钟源控制位置数为1的代码如下:SysTick_CTRL_ENABLE_Msk为使能系统滴答定时器,SysTick_CTRL_CLKSOURCE_Msk就是时钟源控制位置一。      至于上述代码中为什么用了一个“|”(或)呢?      这里我们解释一下这个C语言知识,对于一个8位的寄存器来说,这里我们简单起见,就举8位的寄存器作为例子,这里我们如果要实现修改其中一位,却不改变其他位的值,改如何实现的问题,这里我们具体看。     首先我们默认的8位寄存器的初始值假设我们设置为:1001_0111     那么现在我们比如说要改变第6位的值(这里我们指的是真实的第六位,为什么这么说,按照平常我们的说法寄存器是从第0位到第7位)。     我们的实现方法如下: 1001_0111 | 0010_0000 = 1011_0111     可以看出,通过或的方式我们可以实现只改变一位而不影响其他位的数据。     那么如果我们要让一位相反,从1变到零,我们可以通过与的方式来实现。比如我们这里改变第五位试下。 1001_0111 & 0001_0000 = 0001_0000     看到得到的结果并不是我们想要的,这里需要做一个小小的变通,对应后面要进行相与的数据要进行一个取反,之后才可以进行两者的相与。 1001_0111 & (~(0001_0000)) = 1001_0111 & 1110_1111 = 1000_0111     这样子我们就可以成功实现了不管是置零还是置一都可以不改变其他位的数据,之所以这样做的原因是,430没有办法进行位寻址,所以没有办法对位进行直接操作,只能通过寄存器的方式来进行操作,而对于51来说可以直接进行位寻址。而32实现的方式是通过位绑定的方式进行实现的,这里我们不细说这个位绑定的方式,我们下次再来写一个帖子说明这个问题,了解一下具体的实现方式。     好了,明白了这个我们在看下前面的SysTick->CTRL这个指的是用ARM提供的寄存器指令进行编写的,代表是指向我们SysTick这个模块的控制寄存器,在ARM-Cotex-M4中有四个寄存器来控制SysTick这个模块。我们可以找到他们的说明(这里我没有找到Cotex-M4的中文滴答定时器的说明,但是有Cotex-M3的中文说明,这两者是一样的在滴答定时器这个章节,所以我们引用的是M3的)。     在这里详细解释了我们滴答定时器的四个寄存器,包括相关的控制位功能,我们需要了解这些东西,但是没有必要去背他,我们只需要知道一个学习的方法,在以后需要用到的时候我们懂得找就可以了。毕竟以后当你出去到企业之后,公司做项目,不可能让你去背一个寄存器的每一位每一位是什么意思,不大现实,也毫无意义。所以,方法很重要,要掌握方法。      我们对比到432手册中给出的寄存器描述,同样有四个寄存器,后面内容有点多,这里就不在截图出来了,通过对比我们会发现两者其实是一模一样的(毕竟是同一家出的东西嘛,ARM)。     按照432中给出的配置步骤: 1.我们第一步需要配置STCVR这个寄存器,也就是我们的SysTick->LOAD,第一步要给出重装载的值,这里我们就要明白,这个重装载的值是24位数据,那么我们就可以计算出我们对应的最大的延时数。24位的寄存器最多可以数16_777_216个数据,那么我们根据我们的时钟频率就可以计算出我们的延时时间。如果是48MHz的话,我们最多可以数349_525这么多us,相应的就可以数34.9ms的时间。所以在这个频率下我们要控制我们的延时时间,不能过高。一般情况下这么长的延时时间我们也是够用的。可以看出48MHz的频率来数数,频率还是有点太高,要获得更长的延时,就要降低时钟频率。   2.接下来我们需要配置我们的STCVR这个寄存器,这个寄存器对应我们的SysTick->VAL,是我们当前寄存器的值,我们需要把它清零。   3.最后就是控制状态寄存器了,上面我们已经说明了,需要使能SysTick这个模块,同时把时钟源控制位置一。       这样我们完成了所有的配置工作,这里我们还需要明白的是SysTick是一个24位的自动重装载寄存器,我们使能了他,他就会从STCVR这个装载寄存器中载入要计数的值,然后一直向下数,而不是向上数,一直数到0,数到0之后会置位状态寄存器中的COUNTFLAG标志位,我们也是开启了滴答定时器之后一直查询该标志位,看看是否数完了。     temp&0x01这个查询的是SysTick模块时候使能,查询的是最低位,为什么用&呢?和我们上面说的置1和置0有点类似,实在不行自己写一两个数据验证一下也就明白了。     temp&(1<<16)这个查询的就是我们上面所说的COUNTFLAG这个标志位,那为什么这里1要左移16位呢?我们看寄存器的描述就可以知道,该位是该寄存器的第16位,所以要左移16位来进行相与。     需要同时满足temp&0x01&&!(temp&(1<<16)),这两个条件才可以,完成之后就会退出while循环,然后我们关闭SysTick这个模块,因为他是个自动重装载寄存器,不关闭的话,他会再次从重装载寄存器中取出数据继续数数。     后面的ms延时也是一个道理。就不在解释说明了。     补充说明一个问题,就是我们在配置SysTick->CRTL这个寄存器的时候使用了一个这样的宏定义,如下,前面出现了一个1UL,那么这个UL是什么意思呢?1UL这里指的是无符号长整型数字1,如果不写后缀名的话,系统默认为int型。那为什么这里需要用长整型,是因为这是一个32位的寄存器,整型没有办法表示这么大的需要一个长整型的才可以。后面的SysTick_CTRL_ENABLE_Pos,道理和我们上面的说的查询COUNTFLAG的道理是一样的,左移。     现在我们要验证一下时钟源到底是哪一个?看手册中的FCLK根本不知道是什么我们通过实际操作验证一下就知道了。     这里我们采用的是控制变量法,总共有ACLK、BCLK、HSMCLK、MCLK、SMCLK这几个时钟,把其中一个设置为高频率,其他的都设置为低频率,然后点一个灯,目测下闪烁频率就知道是哪个了。     我们之前已经推算出可能会是MCLK,所以一开始我就直接改了MCLK的频率,很明显的看出灯的闪烁频率发生了很大的改变,一开始我们都是以48MHz为基准,之后我修改为32KHz,中间的落差很大,所以确定SysTick的时钟来源是MCLK,那么现在我们就可以很快的确定我们改如何使用和配置SysTick的延时了。     中间的修改代码的过程和推理判断就不在说明了。大家只要记住结论就好了,SysTick的时钟是来自MCLK。      这里在分享一个调试的技巧,如果你不确定你的配置代码是否正确,需要从寄存器的级别来进行查看的话我们可以通过CCS的Debug窗口中找到对应的寄存器,点开之后会有每一位的变化情况,暂停代码运行既可查看每一位的运行情况。    我们可以得到这样的结果。并且旁边还给出了相应的位进行解释,非常的方便。     最后我们的代码改成下面的,把我们的MCLK的频率选择为高速时钟,在进行16分频,这样就得到比较低的3MHz的频率,这样子就可以进行较长的延时。 同时我们对延时的初始函数进行了修改,这样方便大家理解和使用,其他的代码大家请看附件。     今天就先聊到这里了。

  • 2019-06-09
  • 发表了主题帖: msp430程序库连载系列 12864液晶

         液晶是单片机系统最常用的显示设备之一,这个程序库是在MSP430F169、MSP1430F149单片机上测试通过的,可以放心使用;液晶选用的是金鹏的:OCMJ4X8C型号的12864液晶;控制液晶用的是并行方式,三个控制口是P3.0、P3.1、P3.2三个IO口,数据用的是P5数据IO口。   硬件介绍: 430的数字IO口:      MSP430F149、MSP430F169均有P1-P6 每个8位 共48个IO口;有大量的IO口可供使用,所以对液晶控制可以选用8位平行数据方式;430的每个IO口都是双向IO口,通过寄存器控制其数据传输方向,很方便实用;有关msp430单片机的IO口介绍可以参考德州仪器提供的用户指南和数据手册等资料。   液晶OCMJ4X8C:         此模块可以显示字母、数字符号、中文字型及图形,具有绘图及文字画面混合显示功能。提供三种控制接口,分别是8位微处理器接口,4位微处理器接口及串行接口(OCMJ4X16A/B无串行接口)。所有的功能,包含显示RAM,字型产生器,都包含在一个芯片里面,只要一个最小的微处理系统,就可以方便操作模块。         这款液晶内置2M-位中文字型ROM (CGROM) 总共提供8192个中文字型(16x16点阵),16K位半宽字型ROM(HCGROM) 总共提供126 个符号字型(16x8点阵),64x16位字型产生RAM(CGRAM),另外绘图显示画面提供一个64x256点的绘图区域(GDRAM),可以和文字画面混和显示。   OCMJ4X8C的引脚说明: 引脚 名称 方向 说明 1 VSS - GND(0V) 2 VDD - Supply Voltage For Logic(+5V) 3 NC - Supply Voltage For LCD(悬空) 4 RS(CS) I H:Data L:Instruction Code 5 R/W(STD) I H:Read L:Write 6 E(SCLK) I Enable Signal,高电平有效 7 DB0 I/O 数据0 8 DB1 I/O 数据1 9 DB2 I/O 数据2 10 DB3 I/O 数据3 11 DB4 I/O 数据4 12 DB5 I/O 数据5 13 DB6 I/O 数据6 14 DB7 I/O 数据7 15 PSB I H:Parallel Mode L:Serial Mode 16 NC - 空脚 17 /RST I Reset Signal,低电平有效 18 NC - 空脚 19 LEDA - 背光源正极(+5V) 20 LEDK - 背光源负极(0V)   引脚连接方式:PSB、RST接高电平(3.3v);RS接P3.0;R/W接P3.1;E接P3.2;DB0-DB7接P5口,电源接3.3v(包括背光) GND接地(包括背光)。   汉字图形显示步骤:   1、显示资料RAM(DDRAM)   显示数据RAM 提供64x2 个字节的空间,最多可以控制4 行16 字(64 个字)的中文字型显示,当输入显示资料RAM时,可以分别显示CGROM,HCGROM 与CGRAM 的字型;本系列模块可以显示三种字型,分别是半宽的HCGROM 字型、CGRAM 字型及中文CGROM 字型,三种字型的选择,由在DDRAM 中写入的编码选择,在0000H~0006H 的定字型,02H~7FH 的编码中将选择半编码中将选择CGRAM 的自宽英数字的字型,至于A1 以上的编码将自动的结合下一个字节,组成两个字节的编码达成中文字型的编码。   BIG5(A140~D75F) GB(A1A0~F7FF),详细各种字型编码如下:   1). 显示半宽字型:将8 位资料写入DDRAM 中,范围为02H~7FH 的编码。   2). 显示CGRAM 字型:将16 位资料写入DDRAM 中,总共有0000H,0002H,0004H,0006H 四种编码。   3). 显示中文字形:将16 位资料写入DDRAM 中,范围为A140H~D75FH 的编码(BIG5) , A1A0H~F7FFH 的编码(GB)。将16 位资料写入DDRAM 方式为透过连写入两个字节的资料来完成,先写入高字节(D15~D8)再写入低字节(D7~D0)。   2、绘图RAM(GDRAM)   绘图显示RAM 提供64x32 个字节的记忆空间(由扩充指令设定绘图RAM 地址),最多可以控制256x64点的二维绘图缓冲空间,在更改绘图RAM 时,由扩充指令设定GDRAM 地址先设垂直地址再设水平地址(连续写入两个字节的数据来完成垂直与水平的坐标地址),再写入两个8 位的资料到绘图RAM,而地址计数器(AC)会自动加一,整个写入绘图RAM 的步骤如下:   1). 先将垂直的字节坐标(Y)写入绘图RAM 地址。   2). 再将水平的字节坐标(X)写入绘图RAM 地址。   3). 将D15~D8 写入到RAM 中(写入第一个Bytes)。   4). 将D7~D0 写入到RAM 中(写入第二个Bytes)。   液晶屏显示地址:     有关液晶其他的或详细的介绍,请参考12864液晶的资料。   程序实现: 判忙: 等待液晶模块空闲。 液晶模块要求:当模块在接受指令前,微处理顺必须先确认模块内部处于非忙碌状态,即读取 BF 标志时 BF需为 0,方可接受新的指令;如果在送出一个令前并不检查 BF标志,那幺在前一个指令和这个指令中间必须延迟一段较长的时间,即是等待前一个指令确实执行完成;在这里,我选用等待忙标志结束。程序如下:   void WaitForEnable() {     char busy;       CLR_RS;     SET_RW;       DATA_DIR_IN;       do                       //判忙     {         SET_EN;         _NOP();                             busy = DATA_IN;          CLR_EN;     }     while(busy & 0x80);        DATA_DIR_OUT; } 这样,每次向液晶写命令或数据时,只需先调用此函数即可,该函数将会阻塞,直到忙标志变回0(内部空闲,可以接受命令)。   写入数据: 向模块内部RAM写入数据。 写入数据到DDRAM即可显示到液晶,写入函数:   void LcdWriteData(char data) {     WaitForEnable();       SET_RS;     CLR_RW;       DATA_OUT = data;    //写数据       SET_EN;     _NOP();     CLR_EN; } 同样,调用这个函数也可以向其他RAM写入数据,完成相应操作。   写入命令: 向模块写入命令。 写入命令可以通过液晶的指令集,控制液晶完成相应的功能。程序如下:   void LcdWriteComm(char cmd) {     WaitForEnable();    //检测忙信号?       CLR_RS;     CLR_RW;       DATA_OUT = cmd;    //写命令       SET_EN;     _NOP();     CLR_EN; } 如果cmd是0x80-0x9F,则是向液晶写入地址;地址表参见前面硬件介绍部分。   写入字符串: 写入字符串,以显示。 写入字符串即是多次调用写入数据,把字符串写入液晶以供显示。程序如下:   void LcdWriteString(char addr,char *str) {     LcdWriteComm(addr);     while(*str!='\0')     {         LcdWriteData(*str);         str++;     } } 这是向某个地址写入字符串,液晶显示到相应位置。这个函数有个要求,就是字符串是中文字符串;如果不是,每一处的英文必须两个相连,否则将显示乱码,如果只有一个英文字符,可以加入空格;如:LcdWriteString(0x90,"1 abcd你好啊"); 1+空格+abcd+汉字中 1只有一个字符,加空格,ab,cd两个,直接显示到一个汉字的位置。   液晶初始化:液晶必须初始化之后才能正常使用。 初始化就是一系列命令,完成液晶状态的初始工作,以使液晶可供正常使用。程序如下:   void LcdInit() {     CTRL_DIR_OUT;       DelayNms(500);     LcdWriteComm(0x30);     //基本指令集     LcdWriteComm(0x01);     //清屏,地址00H     LcdWriteComm(0x06);     //光标的移动方向     LcdWriteComm(0x0c);     //开显示,关游标 } 在运行过这个函数之后,液晶方能正常的显示;在调用液晶显示函数前,必须先调用这个函数。   程序实现就先到这儿,还可以加入显示图片等功能;要收拾东西回去了,其他功能暂不实现了,以后需要的时候再加入。   使用示例: 这个程序的使用方式和串口程序库的使用方式一样,把C文件加入工程;H文件包含进要调用的程序源文件中即可。   void main( void ) {     // Stop watchdog timer to prevent time out reset     WDTCTL = WDTPW + WDTHOLD;     ClkInit();     LcdInit();     LcdWriteString(0x90,"1 abcd你好啊"); } 这个函数运行后,将在第二行显示 1 abcd你好啊 字符串,如果把1后面的空格去掉,中文部分将是乱码。ClkInit(); 这个函数和前面一个里面调用的一样,把主系统时钟设为8MHz,SMCLK设为1MHz。    到此,液晶的驱动基本完成,其他功能之后再添加了。   如果有不好或不对的地方,欢迎大家提出,谢谢啦。

  • 发表了主题帖: MSP430学习心得

         参加完TI杯的电子设计竞赛,对MSP430也有了一定了解。正准备着手编写一个MSP430的函数库,没想到网上已经有人做过了。这里将该大牛的博客转载至此,一边学习一边与各位好友共同探讨。   MSP430学习心得<一>      转眼已经大三就要结束了,我的大学生活即将结束;由于本人对软件比较感兴趣,毕业之后也许就远离的我的专业(电子信息科学与技术)了;我在大学期间也参加了电子设计竞赛等,在竞赛中我主要负责单片机程序的编写,所以对msp430系列的单片机比较熟悉;在这个系列的文章里,我主要介绍我对430单片机的理解,整理之前写下的程序,产生一个具有一定通用性的430程序库。       我与msp430最初的接触来自机械工业出版社出版的《MSP430系列单片机系统工程设计与实践》这本书;我开始参加电子设计竞赛是在大二的暑假,放假之前听说竞赛用MSP430F169的单片机,然后就去图书馆找有关430单片机的书籍了,有关这款单片机的书不多,很幸运的是我借到了这本书;我写430单片机的程序风格很大程度上受到了此书的影响。        程序库的组织方式:程序库解决方案包含多个项目,每个项目是针对一个单元(如:uart 异步串行口)的程序库和使用示例,如异步串行口的程序库,下图中UART项目,Uart.c是主要的程序库源代码,Uart.h是对应头文件,使用时需包含此头文件,main.c是使用示例代码。  程序库使用时只需.c文件和对应的.h文件即可。   文件组织方式:程序库的c文件和h文件一一对应,c文件至少包含两个头文件,其中一个是430的头文件,以使用单片机的硬件资源,另一个是其对应的头文件;如Uart.c开头即为  #include   #include "Uart.h"  为防止重复包含头文件中均有#define语句如Uart.h开头和结尾:  #ifndef __UART_H  #define __UART_H    #endif /* __UART_H */   程序库使用方式:第一步,先把c文件和h文件拷到工程文件夹;然后把c文件添加到项目中  在左侧workspace中右击项目,选Add—>Add Files,选择刚添加的c文件;如图:    最后在要调用库函数的程序文件中包含拷进来的头文件;之后,就可以正常调用程序库中的函数(H文件中声明的,需要的话,可以自行添加)。   程序库目前打算先从异步串行口写起,多谢网友们的支持了啊

  • 发表了主题帖: msp430程序库连载系列异步串口USART

    串行通信接口是处理器与其他设备进行数据通信最常用的方式之一。我的这个程序库是针对MSP430f14系列和MSP430f16系列的,我常用的单片机是这两款:msp430f149,msp430f169。这两款单片机中均有两个增强型串行通信接口,都可以进行同步或是异步通信,甚至169的模块USART0还能进行进行I2C协议通信。在这里,我们只讨论异步串行通信。 硬件介绍: MSP单片机的USART模块可以配置成SPI(同步通信)模式或UART(异步通信)模式,这里只讨论UART方式。UART数据传输格式如下:e 起始位,数据位由高到低7/8位,地址位 0/1位,奇偶校验位 奇偶或无,停止位1/2位。数据位位数、地址位、奇偶校验位、停止位均可由单片机内部寄存器控制;这两款单片机都有两个USART模块,有两套独立的寄存器组;以下寄存器命中出现x代表0或是1,0代表对应0模块的寄存器,1代表对应1模块的寄存器;其中,与串口模式设置相关的控制位都位于UxCTL寄存器,与接收相关的控制位都位于UxRCTL寄存器,与发送相关的控制位都位于UxTCTL寄存器;波特率设置用UxBR0、UxBR1、UxMCTL三个寄存器;接收与发送有独立的缓存UxRXBUF、UxTXBUF,并具有独立的移位寄存器和独立的中断;中断允许控制位位于IE1/2寄存器,中断标志位位于IFG1/2寄存器。 波特率设置:430的波特率设置用三个寄存器实现,  UxBR0:波特率发生器分频系数低8位。  UxBR1:波特率发生器分频系数高8位。  UxMCTL:波特率发生器分频系数的小数部分实现。  设置波特率时,首先要选择合适的时钟源:USART模块可以设置的时钟源有UCLK引脚、ACLK、SMCLK;对于较低的波特率(9600以下),可选ACLK作为时钟源,这样,在LPM3(低功耗3)模式下,串口仍能正常发送接收数据;另外,由于串口接收过程有一个三取二判决逻辑,这至少需要三个时钟周期,因此分频系数必须大于3;波特率高于9600时,将不能使用ACLK作为时钟源,要调为频率较高的SMCLK作为时钟源;另外还可以外部输入UCLK时钟。分频系数计算公式如下: 小数分频是MSP430单片机的串口特色之一,UxMCTL寄存器的作用就是控制小数的分频,控制方法如下:对应位是1,则分频系数加一,0则分频系数减一;小数分频器会自动依次取出每一位来调整分频系数。其计算方法:可以先计算小数部分一的个数,然后把1均匀的放入UxMCTL的8位中,这样计算比较简单,分频系数的小数部分乘以8即得到1的位数,查表得到对应的UxMCTL值;另外一种通过计算每一位的错误率,交互计算,直到得到最小错误率的UxMCTL值,这种方法比较复杂,但得到的小数分频误差更小,这种方法也是TI给的计算方法,详细参考UserGuide。 另外,有关寄存器,以及其他单片机硬件有关知识请参考德州仪器提供的用户指南和数据手册等资料。 程序实现: 宏定义:是程序具有更好的移植性。 对模块的寄存器进行宏定义,把0/1换成x,使用时,只需更改宏定义即可更改程序是使用哪个模块;这样程序就具有了比较好的移植性。 /***********************************宏定义*********************************/ #define UxCTL   U0CTL #define UxRCTL  U0RCTL #define UXTCTL  U0TCTL #define UxBR0   U0BR0 #define UxBR1   U0BR1 #define UxMCTL  U0MCTL #define UxRXBUF U0RXBUF #define UxTXBUF U0TXBUF #define UxME    U0ME #define UxIE    U0IE #define UxIFG   U0IFG #define UTXEx   UTXE0 #define URXEx   URXE0 #define URXIEx  URXIE0 #define UTXIEx  UTXIE0 #define UARTON  P3SEL |= 0X30           // P3.4,5 = USART0 TXD/RXD /**************************************************************************/ 程序改为UART1时,只需把宏定义中的0改为1  UARTON改为对应端口的即可 异步串口初始化(UartInit):完成波特率,停止位以及其他相关的设置。 串口初始化,首先是波特率寄存器值的计算和设置:本程序选用第二种:通过运算,选取误差最小的寄存器所需值进行设置。 波特率寄存器值根据所选时钟频率和所需波特率值进行设置,计算方法:从m0(UxMCTL最低位)开始计算,根据这一位的误差(0或1时)误差较小的bit值,直到计算完成。 为了更好的写这个程序,我先用C语言写了一个简单的波特率计算软件,为了让设置波特率的函数能够在单片机程序中复用,程序用宏定义模拟的MSP430单片机的波特率寄存器。完整程序如下: #include<stdio.h> #include<math.h> //函数声明 void SetBaudRateRegisters(long clk,int baud); /************************宏定义***********************/ #define UxBR1    a[0] #define    UxBR0    a[1] #define    UxMCTL    a[2] unsigned char a[3];                     //数组模拟寄存器 void main() {     long clk;                           //时钟     long baud;                           //波特率     printf("\t---波特率计算软件!---\n");     printf("\n请输入时钟频率(Hz):");     scanf("%ld",&clk);                         printf("\n请输入波特率:");     scanf("%ld",&baud);     getchar();                          //读取多余回车符     SetBaudRateRegisters(clk,baud);     //设置寄存器值     //显示寄存器值     printf("\nUxBR1:0x%x\tUxBR0:0x%x\tUxMCTL:0x%x\n",UxBR1,UxBR0,UxMCTL);          getchar(); } /**************************************************************************** * 名    称:SetBaudRateRegisters * 功    能:根据时钟 波特率设置对应寄存器 * 入口参数: *           clk:        所选时钟频率(如:32768)             baud        波特率      (300~115200) * 出口参数:无 * 范    例: SetBaudRateRegisters(32768,9600) //用时钟频率32768产生9600的波特率 ****************************************************************************/ void SetBaudRateRegisters(long clk,long baud) {     int n = clk / baud;     //整数波特率     char mSum = 0;            //Σmi     int txEr0;              //对应位为0时错误率     int txEr1;              //对应位为1时错误率     char i = 0;             //循环计数     UxBR1 = n >> 8;         //高8位     UxBR0 = n & 0xff;       //低8位     UxMCTL = 0;          //循环 比较错误率大小 设置UxMCTL     for(;i < 8;i++)     {         txEr0 = 100 * baud * ((i + 1) * n + mSum) / clk - 100 * (i + 1);         txEr1 = 100 * baud * ((i + 1) * n + mSum + 1) / clk - 100 * (i + 1);         if(abs(txEr1) < abs(txEr0))         {             mSum++;             UxMCTL |= (1<<i);         }     } } 程序可以使用任何的C语言编译器编译运行,可供网友们复用此程序。我使用vs2010编译运行的,运行结果如下: 运行效果很好,和官方给出的值一样,但是也不全都是这样,4800的波特率(时钟:32768)时就不一样,可能是我计算式只是用了发送时的误差计算,没有用接收误差,计算结果稍有出入,如果有兴趣,网友可以自行添加接收误差,判断;应该就和官方给出的数值完全一样了。 初始化函数:初始化函数完成串口时钟源选择,波特率初始化,奇偶校验,数据位,停止位,以及其他相关设置。 时钟源选择:根据波特率选取时钟源,波特率大于9600,选1M的SMCLK时钟(需要初始化时钟系统对应函数参考使用示例),小于9600,选ACLK(32768)以使功耗降低(低功耗3仍能正常收发数据) UxTCTL &=~ (SSEL0+SSEL1);       //清除之前的时钟设置 if(baud<=9600)                  //brclk为时钟源频率 {   UxTCTL |= SSEL0;              //ACLK,降低功耗   brclk = 32768;                //波特率发生器时钟频率=ACLK(32768) } else {   UxTCTL |= SSEL1;              //SMCLK,保证速度   brclk = 1000000;              //波特率发生器时钟频率=SMCLK(1MHz) } 波特率设置:直接调用之前实现的设置寄存器函数即可,当波特率在正常范围外时,返回0。     //------------------------设置波特率-------------------------        if(baud < 300||baud > 115200)   //波特率超出范围     {         return 0;     }     SetBaudRateRegisters();         //设置波特率寄存器 奇偶校验、数据位位数、停止位数设置:比较简单,直接根据参数值设置对应寄存器即可。 //------------------------设置校验位-------------------------   switch(parity) {     case 'n':case'N': UxCTL &=~ PENA;               break;  //无校验     case 'p':case'P': UxCTL |= PENA + PEV;          break;  //偶校验     case 'o':case'O': UxCTL |= PENA; UxCTL &=~ PEV; break;  //奇校验       default :         return(0);                            //参数错误 } //------------------------设置数据位-------------------------     switch(dataBits) {     case 7:case'7': UxCTL &=~ CHAR; break;      //7位数据     case 8:case'8': UxCTL |= CHAR;  break;      //8位数据     default :       return(0);                  //参数错误 }  //------------------------设置停止位-------------------------     switch(stopBits) {     case 1:case'1': UxCTL &= ~SPB;  break;      //1位停止位     case 2:case'2': UxCTL |= SPB;   break;      //2位停止位     default :       return(0);                  //参数错误 } 其他:包括串口收发使能,串口接收和发送中断设置,第二功能打开等。     UARTON;                     //端口使能     UxME |= UTXEx + URXEx;      //发送 接收使能          UCTL0 &= ~SWRST;            // Initialize USART state machine          UxIE |= URXIEx + UTXIEx;    // Enable USART0 RX interrupt  到此,MSP430异步串行口的初始化工作全部完成,如果需要其他的方式,只需对应设置寄存器即可。 写字符(UartxWriteChar):向UARTx模块写(发送)一个字符。 写字符:向串口写入一个字符,通过串口向终端发送一个字符。 void UartWriteChar(char c) {    while (TxFlag==0) UartLpm();  // 等待上一字节发完,并休眠   TxFlag=0;                     //   UxTXBUF=c; } 这个函数根据程序标志TxFlag判断上一字符是否发送完成,此标志位将在发送中断中被置位,表示本字符发送完成。发送中断程序如下: #pragma vector=UARTxTX_VECTOR __interrupt void UartTx () {   TxFlag=1;   __low_power_mode_off_on_exit(); } 发送字符时,先等待上一字符发送完成,然后把字符放入发送缓冲区,待发送完成,中断置标志位,指示发送完成。 读取字符(UartxReadChar):从UARTx模块读取(获取)一个字符。 读取字符和写字符类似:调用读取函数后,等待标志位,接收到字符后,读出来。 char UartReadChar() {    while (RxFlag==0) UartLpm(); // 收到一字节?   RxFlag=0;   return(UxRXBUF); } 同样,RxFlag指示收到一个字符,并且在中断中被置位。中断程序如下: #pragma vector=UARTxRX_VECTOR __interrupt void UartRx() {   RxFlag=1;   /*在这里添加用户中断服务程序代码,如将数据压入接收缓冲等*/   __low_power_mode_off_on_exit(); } 读取函数将阻塞,如果收不到字符,CPU将一直处于低功耗状态。 写字符串(UartxWriteStr):向UARTx模块写(发送)一个字符串。 写字符串只需调用写字符函数即可,比较简单,程序如下: void UartWriteStr(char *s) {     while(*s)     {         UartWriteChar(*s++);     } } 这样,即可调用这个函数通过串口发送字符串。 头文件:头文件把要调用的函数声明放进去,需要使用函数时只需包含此文件,不需要再进行函数声明。 头文件内容如下: #ifndef __UART_H #define __UART_H char UartInit(long baud,char parity,char dataBits,char stopBits); void UartWriteChar(char c); void UartWriteStr(char *s); char UartReadChar(); #endif /* __UART_H */ 其中#ifndef 等预编译用来防止重复包含。 程序调用示例: 要调用这个函数库,首先要包含Uart.h头文件;把Uart.h拷到对应文件夹中,然后在要调用程序的源程序文件中添加文件包含: #include "msp430x16x.h"   //430寄存器头文件 #include "Uart.h"         //串口通讯程序库头文件 然后,在项目中加入Uart.c,把Uart.c拷入项目文件夹中,在项目中添加文件,加入后文件结构大致如下图:           image 如果要用9600以上的波特率,需要把SMCLK设为1M,我的程序调用了以下的这个函数,把MCLK设为8MHz,SMCLK设为1MHz: void ClkInit() {     char i;     BCSCTL1 &= ~XT2OFF;             //打开XT2振荡器     IFG1&=~OFIFG;                   //清除振荡错误标志     while((IFG1&OFIFG)!=0)     {         for(i=0;i<0xff;i++);         IFG1&=~OFIFG;               //清除振荡错误标志     }     BCSCTL2 |= SELM_2+SELS+DIVS_3;  //MCLK为8MHz,SMCLK为1MHz } 调用示例程序如下: ClkInit(); UartInit(38400,'n',8,1); //串口初始化,设置成38400bps,无校验,8位数据,1位停止 _EINT(); UartWriteStr(str); UartWriteChar(0x0d);    //发送"换行"(\r)" UartWriteChar(0x0a);    //发送"回车"(\n)"    UartWriteStr("下面测试串口收发函数\r\n"); while(1)                    //串口测试 {     chr=UartReadChar();     //收1字节     UartWriteChar(chr);     //将收到的数据返回 } 如果是9600以下的波特率,可以不调用时钟系统初始化函数,否则必须调用这个函数,或者用其他的方法把SMCLK频率设为1MHz;初始化完成之后,还要开中断(因为Uart函数库用到了中断);然后才能正常的使用这些函数。 这样,这个程序库就完成了,欢迎讨论

  • 发表了主题帖: msp430程序库连载系列 键盘

        按键是单片机系统最常用的输入设备之一;几乎是只要需要交互输入,就必须有键盘。这篇博客实现了一个通用的键盘程序,只要提供一个读取键值的函数(底层键值),程序将完成消抖、存入队列等一些列处理。同时本程序提供最常用的4*4矩阵键盘的程序,和4个按键的程序。 硬件介绍: 本文主要实现了一个键盘的通用框架,可以很方便的改为不同的键盘函数,这里实现了两种按键4个单独按键和4*4行列扫描的键盘。 4个按键的是这样的:四个按键分别一端接地,另一端接上拉电阻后输入单片机的P1.0-P1.3口;这样,按键按下时,单片机接到低电平,松开时单片机输入信号有上拉电阻固定为高电平。 4*4的按键:行输入信号配有桑拉电阻,无按键时默认电平高电平;列扫描信号线直接接到按键列线;读键时,列扫描信号由单片机给出低电平信号(按列逐列扫描),读取行信号,从而判断具体是哪个按键;电路图大概如下: 图中,IN是键盘的列扫描线,OUT是键盘的输出的行信号线。扫描是也可以按行扫描,这时IN是行扫描线,OUT的按键输出的列信号线。我的程序是按列扫描的(行列扫描原理一样,只是行列进行了交换)。 这里,同时实现了4*4按键的scanf函数的移植,同时,加入了之前实现的液晶的printf函数的移植,搭建了一个可以交互输入输出的完整的一个系统;液晶的printf又加入了函数,实现了退格;可以在输入错误数字的时候退格重新输入。 程序实现: 先说一下程序的结构,程序实现了一个循环队列,用来存放已按下的键值,可以保存最新的四个按键,可以防止按键丢失;程序使用的是中断的方式进行按键,每16ms(用的是看门狗的间隔中断)读一次按键,进行判断键值是否有效,有效则放入队列,等待读取。 循环队列的实现:用数组实现,为判断队满,数组的最后一个元素不用于存储键码值: /**********************宏定义***********************/ #define KeySize     4           //键码值队列 #define Length      KeySize+1   //队列数组元素个数 /***************************************************/ /**********************键值队列*********************/ //可KeySize(Length-1)个键码循环队列占用一个元素空间 char Key[Length]; 入队函数:入队时,队满则出队一个,以保存最新的四个按键。 void AddKeyCode(char keyCode) {     if((rear+1)%Length==front)      //队满     {         front=(front+1)%Length;     //出队一个     }     Key[rear] = keyCode;     rear=(rear+1)%Length; } 出队函数:出队函数即是读取按键的函数,以供其他需要的地方调用。 char ReadKey() {     char temp;     //if(rear==front) return '\0';    //无按键     while(rear==front);     temp = Key[front];     front=(front+1)%Length;     return temp; } KeyProcess:这个函数即是键盘处理函数,需要被每10ms-20ms的时间调用一次的函数,在这里把它放入了看门狗定时器16ms的中断中;函数流程图和函数内容如下:   void KeyProcess() {     static char keyValue = 0xff;    //按键标识,键值     static char addedFlag = 0;      //加入队列标志     char keyVal = GetKey();     if(keyVal==0xff)                //无按键     {         keyValue = 0xff;         addedFlag = 0;         return;     }     if(keyValue==0xff)              //之前状态无按键     {         keyValue = keyVal;         return;     }     if(keyValue!=keyVal)            //和前次按键不同     {         keyValue = keyVal;          //保存新按键值         return;     }     if(addedFlag==1)                //已加入队列     {         return;     }     addedFlag = 1;     AddKeyCode(KeyCode[keyVal]); } 这个函数完成按键的判断,并和上次的比较,从而判断是否是有效按键,再根据是否已经入队保存,去判断是否要保存,入队列保存按键。 这个函数需要每10ms-20ms中断运行一次: #pragma vector=WDT_VECTOR __interrupt void WDT_ISR() {     KeyProcess(); } 这是430看门狗的间隔定时中断,设置的是每16ms中断一次:     WDTCTL=WDT_ADLY_16;   //看门狗内部定时器模式16ms     IE1 |= WDTIE;         //允许看门狗中断 KeyProcess里调用了GetKey函数,这个函数需要用户提供,以满足特殊的按键需求,这里提供了两个实例:4个按键和4*4矩阵键盘。 4个按键的getkey函数: char GetKey() {     if((P1IN&0X0F)==0x0E)     {         return 0;     }     if((P1IN&0X0F)==0x0D)     {         return 1;     }     if((P1IN&0X0F)==0x0B)     {         return 2;     }     if((P1IN&0X0F)==0x07)     {         return 3;     }     return 0xff; } 这里根据每个按键,输出按键原始键值,没有按键则输出0xff;当自己提供getkey函数时,也需要这样,无按键时返回0xff 把对应原始键值翻译成所需键码,用数组KeyCode: char KeyCode[] = "0123";    /*4个按键时*/ 这里把它转化成ASCII码输出,需要的话可以自行更改。 4*4矩阵键盘:getkey: char GetKey() {     P1DIR |= 0XF0;                  //高四位输出     for(int i=0;i<4;i++)     {         P1OUT = 0XEF << i;         for(int j=0;j<4;j++)         {             if((P1IN&(0x01<<j))==0)             {                 return (i+4*j);             }         }     }     return 0xff; } 这里是按列扫描,可以随意改成其他扫描方式,只要获取原始键值即可,无按键是须返回0xff。 KeyCode,翻译成ASCII码: char KeyCode[] = "0123456789ABCDEF" 到这里,正常的键盘程序结束,调用时只需加入Key.c,包含Key.h即可使用,先调用KeyInit后,就可以正常的读键了。这里不再细说。 scanf移植:scanf移植时,需要的是ASCII码字符型设备,利用ASCII码输入数据还必须要有回车键,只有这样,才能用scanf输入数据,这里为了输入数据错误时,可以退格修改,按键还有一个退格键。 键盘结构:   1 2 3 退格 4 5 6 保留 7 8 9 保留 保留 0 保留 回车 保留键用字符’\0’,回车’\n’退格’\b’ 所以:KeyCode: char KeyCode[] = "123\b456\000789\0\0000\0\r"; /* 4*4,scanf移植*/ 在字符串里,\0后面是数字时,必须用’\000’否则,c语言编译器认为\0和后面的数字组合为一个字符。 scanf的移植,需要实现getchar函数,这里和之前的getchar函数类似,把它放到了Getchar.c文件里,内容如下: #include <stdio.h> #include "Key.h" #define LINE_LENGTH 20          //行缓冲区大小,决定每行最多输入的字符数 /*标准终端设备中,特殊ASCII码定义,请勿修改*/ #define InBACKSP 0x08           //ASCII  <--  (退格键) #define InDELETE 0x7F           //ASCII <DEL> (DEL 键) #define InEOL '\r'              //ASCII <CR>  (回车键) #define InLF '\n'                 //ASCII <LF>  (回车) #define InSKIP '\3'             //ASCII control-C #define InEOF '\x1A'            //ASCII control-Z #define OutDELETE "\x8 \x8"     //VT100 backspace and clear #define OutSKIP "^C\n"          //^C and new line #define OutEOF "^Z"             //^Z and return EOF int getchar() {     static char inBuffer[LINE_LENGTH + 2];      //Where to put chars     static char ptr;                            //Pointer in buffer     char c;          while(1)     {         if(inBuffer[ptr])                       //如果缓冲区有字符             return (inBuffer[ptr++]);           //则逐个返回字符         ptr = 0;                                //直到发送完毕,缓冲区指针归零         while(1)                                //缓冲区没有字符,则等待字符输入         {             c = ReadKey();                      //等待接收一个字符==移植时关键             if(c == InEOF && !ptr)              //==EOF==  Ctrl+Z              {                                   //只有在未入其他字符时才有效                 printf(OutEOF);                 //终端显示EOF符                 return EOF;                     //返回 EOF(-1)             }             if(c==InDELETE || c==InBACKSP)      //==退格或删除键==             {                 if(ptr)                         //缓冲区有值                 {                     ptr--;                      //从缓冲区移除一个字符                     printf(OutDELETE);          //同时显示也删掉一个字符                 }             }             else if(c == InSKIP)                //==取消键 Ctrl+C ==             {                 printf(OutSKIP);                //终端显示跳至下一行                 ptr = LINE_LENGTH + 1;          //==0 结束符==                 break;             }             else if(c == InEOL||c == InLF)      //== '\r' 回车=='\n'回车             {                 putchar(inBuffer[ptr++] = '\n');//终端换行                 inBuffer[ptr] = 0;              //末尾添加结束符(NULL)                 ptr = 0;                        //指针清空                 break;             }             else if(ptr < LINE_LENGTH)          //== 正常字符 ==             {                 if(c >= ' ')                    //删除 0x20以下字符                 {                     //存入缓冲区                     putchar(inBuffer[ptr++] = c);                 }             }             else                                //缓冲区已满             {                 putchar('\7');                  //== 0x07 蜂鸣符,PC回响一声             }         }     } } 这里是支持退格等键的详细函数。 如果不需要支持退格,可以简化为: int getchar() {     return ReadKey(); } 要实现scanf调用,还需要设置,详细设置参考:MSP430程序库<四>printf和scanf函数移植;需要把库设置为CLIB;在Option-general option-library configuration里面。 这样,键盘的scanf移植完成,需要使用时,只需加入对stdio.h文件的包含,然后完成键盘的初始化即可。 使用示例: 这里,示例实现的是键盘和液晶的简单交互;键盘输入数据,液晶正常显示;就像c语言调试时键盘和屏幕一样;当然没有那个丰富啦。 液晶的部分,用的是原来实现的程序,在这里,为了支持输入错误时退格,对原来的printf函数加入了退格支持。具体参考:MSP430程序库<四>printf和scanf函数移植(已经更新)。 项目中接入液晶的c程序文件和printf的程序文件(Lcd12864.c、Printf.c),加入Lcd12864.h的文件包含;初始化液晶后,就可用printf向液晶输出要显示的内容了。 键盘:加入Key.c,包含Key.h,加入Getchar.c,程序中初始化键盘;然后设置所用的lib为CLIB,具体设置见:MSP430程序库<四>printf和scanf函数移植。之后就可以用键盘和液晶完成和430单片机简单的交互了。 详细参考示例工程和main.c。 #include <msp430x16x.h> #include <stdio.h> #include "Lcd12864.h" #include "Key.h" long a; void main( void ) {     // Stop watchdog timer to prevent time out reset     WDTCTL = WDTPW + WDTHOLD;     ClkInit();     LcdInit();     KeyInit();     _EINT();     while(1)     {         printf("请输入数字:");         scanf("%ld",&a);         printf("输入的数字是:%ld",a);         _NOP();     } } 这样,就可以用键盘向单片机输入数据,同时利用液晶可以很容易的知道数据输入的是否有问题。 键盘的程序库就到这里,有什么不足,欢迎讨论。

  • 发表了主题帖: MSP430FR2355 混合信号微控制器

    本帖最后由 灞波儿奔 于 2019-6-9 22:21 编辑      MSP430FR215x 和 MSP430FR235x 微控制器 (MCU) 均属于 MSP430™MCU 超值系列超低功耗低成本器件产品系列,该产品系列适用于检测和测量 解决方案。MSP430FR235x MCU 集成了四个称之为智能模拟组合的可配置信号链模块,每个组合均可用作 12 位 DAC 或可配置可编程增益运算放大器,以满足系统的特定需求,同时缩减 BOM 并减小 PCB 尺寸。该器件还包含一个 12 位 SAR ADC 和两个比较器。MSP430FR215x 和 MSP430FR235x MCU 都支持 –40° 至 105°C 的扩展温度范围,因此更高温度的工业 应用 可从这些器件的 FRAM 数据记录功能受益。该扩展温度范围使开发人员可以满足烟雾探测器、传感器变送器和断路器等 应用 的要求。       MSP430FR215x 和 MSP430FR235x MCU 具有功能强大的 16 位 RISC CPU、16 位寄存器和常数发生器,有助于实现最大编码效率。数控振荡器 (DCO) 通常可以使器件在不到 10µs 的时间内从低功耗模式唤醒至激活模式。 MSP430 超低功耗 (ULP) FRAM 微控制器平台将独特的嵌入式 FRAM 和整体超低功耗系统架构相结合,从而使系统设计人员能够在降低能耗的情况下提升性能。FRAM 技术将 RAM 的低功耗快速写入、灵活性和耐用性与闪存的非易失性相结合。 MSP430FR215x 和 MSP430FR235x MCU 由广泛的硬件和软件生态系统提供支持,随附参考设计和代码示例,便于您快速开始设计。开发套件包括 MSP-EXP430FR2355LaunchPad™开发套件和 MSP-TS430PT48 48 引脚目标开发板。TI 还提供免费的 MSP430Ware™ 软件,该软件以 Code Composer Studio™ IDE 台式机和云版本组件的形式提供(位于 TI 资源浏览器)。MSP430 MCU 还通过 E2E™ 社区论坛提供广泛的在线配套资料、培训和在线支持。 有关完整的模块说明,请参阅《MSP430FR4xx 和 MSP430FR2xx 系列器件用户指南》。   特性 嵌入式微控制器 16 位 RISC 架构,频率最高可达 24MHz 扩展温度范围:–40°C 至 105°C 3.6V 至 1.8V 的宽电源电压范围(工作电压受限于 SVS 电平,参阅 VSVSH- 和 VSVSH+,见 PMM、SVS 和 BOR) 经优化的低功耗模式(3V 时) 工作模式:142µA/MHz 待机: 具有 32768Hz 晶体的 LPM3:1.43µA(SVS 处于启用状态) 具有 32768Hz 晶体的 LPM3.5:620nA(SVS 处于启用状态) 关断 (LPM4.5):42nA(SVS 处于启用状态) 低功耗铁电 RAM (FRAM) 容量高达 32KB 的非易失性存储器 内置错误修正码 (ECC) 可配置的写保护 对程序、常量和存储的统一存储 耐写次数达 1015 次 抗辐射和非磁性 易于使用 20KB ROM 库包含驱动程序库和 FFT 库 高性能模拟 一个 12 通道 12 位模数转换器 (ADC) 内部共享基准(1.5、2.0 或 2.5V) 采样与保持 200ksps 两个增强型比较器 (eCOMP) 集成 6 位数模转换器 (DAC) 作为基准电压 可编程迟滞 可配置的高功率和低功率模式 一个具有 100ns 的快速响应时间 一个具有 1µs 的响应时间以及 1.5µA 的低功耗 四个智能模拟组合 (SAC-L3)(仅限 MSP430FR235x 器件) 支持通用运算放大器 (OA) 轨至轨输入和输出 多个输入信号选项 可配置的高功率和低功率模式 可配置 PGA 模式支持 同相模式:×1、×2、×3、×5、×9、×17、×26、×33 反相模式:×1、×2、×4、×8、×16、×25、×32 用于进行失调电压和偏置设置的内置 12 位基准 DAC 具有可选基准电压的 12 位电压 DAC 模式 智能数字外设 三个 16 位计时器,每个计时器有 3 个捕捉/比较寄存器 (Timer_B3) 一个 16 位计时器,每个计时器有 7 个捕捉/比较寄存器 (Timer_B7) 一个仅用作计数器的 16 位实时钟计数器 (RTC) 16 位循环冗余校验器 (CRC) 中断比较控制器 (ICC),可启用嵌套硬件中断 32 位硬件乘法器 (MPY32) 曼彻斯特编解码器 (MFM) 增强型串行通信 两个增强型 USCI_A (eUSCI_A) 模块支持 UART、IrDA 和 SPI 两个增强型 USCI_B (eUSCI_B) 模块支持 SPI 和 I2C 时钟系统 (CS) 片上 32kHz RC 振荡器 (REFO) 带有锁频环 (FLL) 的片上 24MHz 数控振荡器 (DCO) 室温下的精度为 ±1%(具有片上基准) 片上超低频 10kHz 振荡器 (VLO) 片上高频调制振荡器 (MODOSC) 外部 32kHz 晶振 (LFXT) 外部高频晶体振荡器,频率最高可达 24MHz (HFXT) 可编程 MCLK 预分频器(1 至 128) 源自具有可编程预分频器(1、2、4 或 8)的 MCLK 的 SMCLK 通用输入/输出和引脚功能 48 引脚封装上的 44 个 I/O 32 个中断引脚(P1、P2、P3 和 P4)可以将 MCU 从 LPM 唤醒 开发工具和软件(另外请参阅工具与软件) LaunchPad™开发套件 (MSP‑EXP430FR2355) 目标开发板 (MSP‑TS43048PT) 免费的专业开发环境 系列成员(另请参阅器件比较) MSP430FR2355:32KB 的程序 FRAM、512B 的数据 FRAM、4KB 的 RAM MSP430FR2353:16KB 的程序 FRAM、512B 的数据 FRAM、2KB 的 RAM MSP430FR2155:32KB 的程序 FRAM、512B 的数据 FRAM、4KB 的 RAM MSP430FR2153:16KB 的程序 FRAM、512B 的数据 FRAM、2KB 的 RAM 封装选项 48 引脚:LQFP (PT) 40 引脚:VQFN (RHA) 38 引脚:TSSOP (DBT) 32 引脚:VQFN (RSM)

  • 回复了主题帖: 一图看懂5G比4G强在哪,附带一项挑战

    strong161 发表于 2019-6-8 14:13 其实真要舞弊传送信息考虑用24G和77G定向发射嘛,频率高,方向正可以前少被探测性,又是汽车开放频段,也没 ...
    说的很好,是这样

  • 2019-06-07
  • 发表了主题帖: 一图看懂5G比4G强在哪,附带一项挑战

         这几天“5G”这个词大家经常碰到,例如“华为5G技术领先,别人2~3年也赶不上”,“川普督促美国要加紧搞5G/6G”,以及昨天的“工信部发了4张5G商用牌照”,“中国进入5G元年”等等。      那么第五代移动通信技术(5G),到底比现在用的4G强在哪呢?且看下图。      上图可以看出5G各项性能指标均优于4G,但是有一项“挑战”出现了,那就是维护高考考场公平的一个“暗中”功臣:信号屏蔽器。技术的发展给利用技术作弊的一方提供了便利,也给对抗的另一方提出了挑战。矛和盾的较量不会停歇。         温馨提醒:今天是高考的第一天,如果你在家却发现手机信号不好,不要慌,不要迁怒运营商,可能是你家正好在学校附近,信号遭到了屏蔽。       一般考场的信号屏蔽并不是将附近的基站关掉,而是在考场加装信号屏蔽器,属于通信干扰的范畴,由于各大运营商分配的频段是已知的,手机信号屏蔽器只要确保在一定的小范围内实现通信信号的屏蔽。毕竟干扰源处于保护区域之中,具有极大的功率优势。 通信干扰的技术也有很多,例如宽带噪声干扰、部分频段的噪声干扰、多音干扰、扫频干扰、跟踪干扰、灵巧干扰等等。        现代通信技术采用了先进的抗干扰技术,例如跳频、直接序列扩频(DSSS)等技术,但是跳频和扩频对干扰的抑制主要靠中频滤波,然而由于前端高频放大器和混频器都是非线性器件,在有强干扰时会恶化,即产生射频前端的阻塞。因此,一般只要频段能覆盖到就较容易干扰。 除了手机信号,屏蔽器也要考虑WIFI的频段2.4~ 2.4835GHz与5.735~ 5.835GHz。       另外,如果出现自制的非法的无线电收发设备,恶意使用频段,那搭载无线电侦测设备的无人机就要出动了,再来一个辐射源定位,抓现行。      当5G商用后,明年的信号屏蔽器也该要升级了啊!基本上要覆盖6GHz以下频段,毫米波段要覆盖24.25~42.5GHz,挑战不可谓不大。        技术本无错,请不要用来图一己私利,而是要用来造福全人类,构建“人类命运共同体”。这个说着有点大,主要还是想说:祝所有的考生考出自己的好成绩,不要低估通信干扰技术,不要抱有侥幸心理,更不能走歪门邪路。

  • 2019-06-06
  • 发表了主题帖: TMS320C6416定时器1中断的使用

    软件开发环境环境:CCS3.1,在CCS Setup中设置成Simulator模式 CPU:TMS320C6416   工程共包含三个文件:main.c,Vectors.asm,BootLoader.cmd 另外还添加了一个库文件:rts6400.lib,根据我的CCS安装目录,该文件位于C:\CCStudio_v3.1\C6000\cgtools\lib目录下   【源文件main.c】 //由于使用了printf函数 #include <stdio.h>    /*定义控制寄存器*/ extern cregister volatile unsigned int AMR;     /* Address Mode Register      */ extern cregister volatile unsigned int CSR;     /* Control Status Register    */ extern cregister volatile unsigned int IFR;     /* Interrupt Flag Register    */ extern cregister volatile unsigned int ISR;     /* Interrupt Set Register     */ extern cregister volatile unsigned int ICR;     /* Interrupt Clear Register   */ extern cregister volatile unsigned int IER;     /* Interrupt Enable Register  */ extern cregister volatile unsigned int ISTP;    /* Interrupt Service Tbl Ptr  */ extern cregister volatile unsigned int IRP;     /* Interrupt Return Pointer   */ extern cregister volatile unsigned int NRP;     /* Non-maskable Int Return Ptr*/ extern cregister volatile unsigned int IN;      /* General Purpose Input Reg  */ extern cregister volatile unsigned int OUT;     /* General Purpose Output Reg */ /* 定义中断选择寄存器 */ #define MUXH 0x019C0000 #define MUXL 0x019C0004 #define EXTPOL 0x019C0008 /*定义定时器1寄存器*/ #define CTL1 0x01980000     //Timer1 control register #define PRD1 0x01980004     //Timer1 period register #define CNT1 0x01980008     //Timer1 counter register int counter = 0;//记录中断次数 interrupt void xint0_isr(void) {  counter ++;  printf("xint0_isr:%d\n",counter); } void Interrupt_Init(void) {  ICR = 0x00000400;   *( volatile unsigned int* )MUXH=0x7fff7fe2;//DSP中断10分配给Timer中断   *( volatile unsigned int* )CTL1= 0x00000201;  //计数器功能设置   *( volatile unsigned int* )PRD1= 0x1000;   //计数器周期值 } void Interrupt_Start(void) {   IER |= 0x00000402;   // IE10=1    CSR |= 0x00000001;   // 全局中断使能   *( volatile unsigned int* )CTL1|= 0x000000C0;  //计数器清零,启动  } void main() {  Interrupt_Init();  printf("interrupt init done!\n");  Interrupt_Start();  while(1)  {   ;  } }   【源文件Vectors.asm】     .ref       _c_int00   .ref     _xint0_isr   ; timer 1 interrupt handler      .sect ".vectors" RESET_RST:     mvkl .S2 _c_int00, B0     mvkh .S2 _c_int00, B0     B    .S2 B0  NOP  NOP  NOP  NOP     NOP NMI_RST:  b  nrp  NOP  NOP  NOP  NOP  NOP  NOP  NOP RESV1:    NOP  NOP  NOP  NOP  NOP  NOP  NOP  NOP RESV2:   NOP  NOP  NOP  NOP  NOP  NOP  NOP  NOP INT4:    b  irp   NOP  NOP  NOP  NOP  NOP  NOP  NOP INT5:     b  irp   NOP  NOP  NOP  NOP  NOP  NOP  NOP      INT6:     b  irp  NOP  NOP  NOP  NOP  NOP  NOP  NOP INT7:     b  irp    NOP  NOP  NOP  NOP  NOP  NOP  NOP INT8:     b  irp    NOP  NOP  NOP  NOP  NOP  NOP  NOP INT9:     b  irp   NOP  NOP  NOP  NOP  NOP  NOP  NOP INT10:     b  _xint0_isr   NOP  NOP  NOP  NOP  NOP  NOP  NOP INT11:     b  irp   NOP  NOP  NOP  NOP  NOP  NOP  NOP INT12:     b  irp   NOP  NOP  NOP  NOP  NOP  NOP  NOP  INT13:     b  irp   NOP  NOP  NOP  NOP  NOP  NOP  NOP INT14:     b  irp   NOP  NOP  NOP  NOP  NOP  NOP  NOP INT15:     b  irp   NOP  NOP  NOP  NOP  NOP  NOP  NOP   【源文件BootLoader.cmd】 -c -o jbb0523.out -m jbb0523.map -heap 0x2000 -stack 0x4000 -l rts6400.lib MEMORY {   BOOT_RAM:  origin = 00000000h, length = 00000400h    IRAM: origin =00000400h length =200000h  SRAM: origin =80000000h length =400000h } SECTIONS {  .vectors : > BOOT_RAM   .vec     : > IRAM   .text    : > IRAM  .cinit   : > IRAM  .stack   : > IRAM  .bss     : > IRAM  .far     : > IRAM  .sysmem  : > IRAM  .cio     : > IRAM  .switch  : > IRAM  .const   : > IRAM  .heap       : > IRAM }  

  • 发表了主题帖: 学习笔记之TMS320C6748的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框图 从中可以看到控制GPIO的各种寄存器,本文只介绍DIR、SET_DATA、CLR_DATA、INDATA这四个寄存器。 三、初始化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了!  

  • 发表了主题帖: TMS320DM8127 摄像机入门套件 (CSK)

    借助 TMS320DM8127 摄像机入门套件 (CSK),开发人员可立即开始评估 DM 数字媒体处理器,并开始设计无线或有线连接的数字视频应用。 DM8127 上的集成式 C674x DSP 十分适合视觉分析处理,用于增强终端设备(例如安全摄像机、车用数字录像机、运动和可穿戴摄像机、无人机、视频会议、数字标牌、立体摄像机和其他数字视频产品)的实时功能。 TMS320DM8127 摄像机入门套件 (CSK) 包含一个安装于 I/O 载板的 TMS320DM8127 处理器板插槽。该处理器板采用小型 (60mm x 44mm) 设计,包含 TMS320DM8127 器件、电源组件、存储器和 TI 的 WiLink 8 WiFi/蓝牙/BLE 模块。它旨在用作生产系统的硬件参考。该载板为通用型板,如果需要,可与其他 DM 处理器板一起使用。随附 Leopard Imaging 传感器和 SD 卡预装 IPNC 软件开发者套件中随附的演示软件可通过无线连接在数分钟内实现 1080p 压缩视频的流式传输。   特性 基于 TMS320DM8127 数字媒体处理器的开发板(处理器板 60mm x 44mm 和通用载板 90mm x 90mm) 集成式 WiLink8 单频带组合 2x2 MIMO WiFi、蓝牙和蓝牙智能(低能耗)模块 预装于随附 SD 卡上的 Linux 4.4 LTS 内核 IPNC 软件开发套件可实现快速应用开发。 具有摄像机捕捉、视频编码和无线传输等开箱即用示范 通用型载板可与其他 DM 器件系列处理器板一起使用 使用随附和预集成的 Leopard Imaging LI-CAM_AR0331-1.8 高分辨率摄像机快速开始捕捉 1080p 视频     包含项目   TMDSCSKCC - Supplier provides kit containing: I/O common carrier board (TMDSCSKCC) Camera module (Leopard Imaging LI-CAM-AR0331-1.8) Power supply cable suitable for lab power supply Ethernet cable Component video cable USB cable SD card containing software, executables, manuals, and out-of-box demo USB SD card reader Quick Start Guide Warranty card TMDSCSK8127 - Supplier provides kit containing: TMS320DM8127 processor board SD card containing software, executables, manuals, and out-of-box demo Quick Start Guide Warranty card This Camera Starter Kit (CSK) does not include a power supply. Click here to purchase a power supply compatible with this CSK. 技术文档 数据表 (1) 标题 类型 大小 (KB) 日期 下载最新英文版本  TMS320DM812x DaVinci Video Processors 数据表 PDF 2490 2014年 3月 25日   用户指南 (2) *This is not an TI official document. 标题 类型 大小 (KB) 日期 下载最新英文版本 IPNC Reference Design Kit Installation Guide Wiki*   2017年 1月 5日    DM8127 Camera Starter Kit PDF 5575 2016年 12月 9日   设计文件 (2) 标题 类型 大小 (KB) 日期 下载最新英文版本 TMDSCSKCC Reference Design ZIP 196559 2017年 3月 23日   TMDSCSK8127 Reference Design ZIP 128172 2017年 3月 23日   更多文献资料 (2) 标题 类型 大小 (KB) 日期 下载最新英文版本  TMDSCSK8127 EU Declaration of Conformity (DoC) PDF 38 2019年 1月 2日    DM8127 Quick Start Guide PDF 1241 2017年 1月 5日   相关产品 软件 (1) 名称 器件型号 软件类型 适用于 DM 摄像机入门套件 (CSK) 的 IPNC RDK 软件  IPNC-RDK-CSK  软件开发套件 (SDK)    TI 器件 (10) 器件型号 名称 产品系列 CSD17313Q2  30V N 通道 NexFET™ 功率 MOSFET  MOSFET的  SN74AVC4T245  具有可配置电压转换和三态输出的 4 位双电源总线收发器  电压电平转换  SN74LVC1G08  单路 2 输入正与门  门  SN74LVC1G11  单路 3 输入正与门  门  TMS320DM8127  DaVinci 数字媒体处理器  数字信号处理器(DSP)  TPS51216  DDR2/3/3L/4 存储器电源解决方案同步降压控制器,2A LDO,缓冲参考  电源管理  TPS62353  Adjustable, 800mA, 3MHz Buck Converter with I2C Interface in Chip Scale Package  降压(降压)  TPS65911  采用 6mm x 6mm QFN 封装的集成电源管理 IC (PMIC),具有 4 个 DC/DC、9 个 LDO 以及 RTC  电源管理  TXS0108E  8 位双向电压电平转换器,适用于漏极开路和推挽应用  电压电平转换  WL1837MOD  WiLink™ 8 工业双频段、2x2 MIMO Wi-Fi、Bluetooth 和 Bluetooth 低耗能模块  SimpleLink 解决方案 

最近访客

< 1/3 >

统计信息

已有80人来访过

  • 芯币:1389
  • 好友:1
  • 主题:466
  • 回复:26
  • 课时:--
  • 资源:--

留言

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


现在还没有留言