fish001

  • 2019-07-18
  • 发表了主题帖: 时钟振荡电路分析

           时钟振荡电路用于产生单片机正常工作时所需要的时钟信号。51系列单片机可以采用两种方式的时钟振荡电路:内部振荡电路和外部振荡电路。下面分别介绍这两种方式。   内部振荡电路   内部振荡电路采用单片机内部振荡器来产生工作所需的时钟。51系列单片机内部包含一个高增益的单级反相放大器,引脚XTAL1和XTAL2分别为片内反相放大器的输入端口和输出端口。当单片机工作于内部时钟模式的时候,只需在XTAL1引脚和XTAL2引脚连接一个晶体振荡器或者陶瓷振荡器,并通过两个电容后接地即可,如图2.2所示。使用时,对于电容的选择有一定的要求。     图2.2 内部时钟模式   当外接晶体振荡器的时候,接地电容一般选择C1=C2=30±10pF。   当外接陶瓷振荡器的时候,接地电容一般选择C1=C2=40±10pF。   在实际的硬件电路板设计时,应该保证外接的振荡器和电容尽可能靠近单片机的XTAL1和XTAL2引脚。这样可以减少寄生电容的影响,使振荡器能够稳定可靠地为单片机CPU提供时钟信号。如果振荡器连接不当,会导致电路不起振,没有时钟信号产生。   外部振荡电路   外部振荡电路是采用外部振荡器产生时钟信号直接供单片机使用。对于不同结构的单片机,外部振荡电路的方式有所不同,如图2.3所示。   图2.3 外部振荡电路   对于普通的8051单片机,外部时钟信号由XTAL2引脚接入后直接送到单片机内部的时钟发生器,而引脚XTAL1则应直接接地。这里需要注意,由于XTAL2引脚的逻辑电平不是TTL信号,因此建议外接一个上拉电阻。   对于CMOS型的80C51、80C52、AT89S52等单片机,其内部的时钟发生器的信号取自于反相放大器的输入端。因此,外部的时钟信号应该接到单片机的XTAL1引脚,而XTAL2引脚悬空即可。   另外,无论采用内部振荡电路还是外部振荡电路,振荡电路的频率应该满足单片机的工作频率要求,比如对于AT89S52单片机,其工作频率为0~33MHz。

  • 发表了主题帖: 模拟电路知识之三极管的基本用法

          三极管是模拟电路最常用的基础知识,三极管是电路设计中应用非常广泛的半导体元器件,若三极管都不会用,模电相当于白学了。   三极管分为NPN和PNP两种类型,三极管由两个PN结组成,NPN三极管为两个PN结共用一个P区(也称基区),在构造工艺上,基区做得非常薄,一般为几微米到几十微米,两个 PN 结依靠基区紧密地结合在一起,相互影响和关联,使得三极管的特性完全不是两个单独PN结特性的叠加,三极管外部施加电压后,基极电流、集电极电流和发射极电流之间的关系,形成电流放大作用。   三极管的放大倍数与其物理结构有关,选择三极管时放大倍数β是必须考虑的重要技术指标之一,   三极管的电流方向以及三个极之间的电流关系如下图,IC=βIB,IE=IB+IC     三极管有饱和、放大和截止三个工作区间,下面介绍一些常见的用法;   (1)驱动放大,一般单片机、DSP、ARM、CPLD/FPGA等CPU的IO口驱动电流比较弱,无法直接驱动负载,最常用的是使用三极管进行电流驱动能力放大,如下图所示;   示意原理驱动LED灯,可以根据实际需要变换为其它负载,比如继电器、电机等;+5V电源也可以改变,比如12V/24V等,但所选三极管必须能够承受这个电压。   (2)逻辑取反,如下图所示,使用NPN三极管进行取反,十分方便,输入为高电平时,输出为低;输入为低电平时,输出为高(5V)。      (3)当电子开关使用,当电子开关时一般使用三极管的饱和区,通过控制基极使三极管处于饱和或截止区,从而实现三极管开通或断开,起到开关的作用。

  • 发表了主题帖: 传统差动放大器的缺点及解决方案

         经典的分立差动放大器设计非常简单,一个运算放大器和四电阻网络有何复杂之处?经典的四电阻差动放大器如图1所示,但是这种电路的性能可能不像设计人员想要的那么好。本文从实际生产设计出发,讨论了与分立电阻相关的一些缺点,包括增益精度、增益漂移、交流共模抑制(CMR)和失调漂移等方面。   图1. 经典分立差动放大器   该放大器电路的传递函数为:   若R1 = R3且R2 = R4,则公式1简化为:   这种简化有助于快速估算预期信号,但这些电阻绝不会完全相等。此外,电阻通常有低精度和高温度系数的缺点,这会给电路带来重大误差。   例如,使用良好的运算放大器和标准的1%、100ppm/°C增益设置电阻,初始增益误差最高可达2%,温度漂移可达200ppm/°C。为解决这个问题,一种解决方案是使用单片电阻网络实现精密增益设置,但这种结构很庞大且昂贵。除了低精度和显著的温度漂移之外,大多数分立差动运算放大器电路的CMR也较差,并且输入电压范围小于电源电压。此外,单片仪表放大器会有增益漂移,因为前置放大器的内部电阻网络与接入RG引脚的外部增 益设置电阻不匹配。   解决所有这些问题的最佳办法是使用带内部增益设置电阻的差动放大器,例如AD8271。通常,这些产品由高精度、低失真运算放大器和多个微调电阻组成。通过连接这些电阻可以创建各种各样的放大器电路,包括差动、同相和反相配置。芯片上的电阻可以并联连接以提供更广泛的选项。相比于分立设计,使用片内电阻可为设计人员带来多项优势   图2. 增益误差与温度的关系——AD8271与分立解决方案比较   交流性能   在电路尺寸方面,集成电路比印刷电路板(PCB)小得多,因此相应的寄生参数也较小,对交流性能有利。例如,AD8271运算放大器的正负输入端有意不提供输出引脚。这些节点不连接到PCB上的走线,电容保持较低,从而提高环路稳定性并优化整个频率范围内的共模抑制。性能比较参见图3。   图3. CMRR与频率的关系——AD8271与分立解决方案CMRR比较   差动放大器的一项重要功能是抑制两路输入的共模信号。参考图1,如果电阻R1至R4不完全匹配(或者当增益大于1时,R1、R2和R3、R4的比率不匹配),那么部分共模电压将被差动放大器放大,并作为V1和V2之间的有效差压出现在VOUT处,其无法与实际信号相区分。如果电阻不理想,那么部分共模电压将被差动放大器放大,并作为V1和V2之间的有效差压出现在VOUT处,其无法与实际信号相区分。   差动放大器抑制这一部分电压的能力称为共模抑制。该参数可以表示为共模抑制比(CMRR)或转换为分贝(dB)。分立解决方案的电阻匹配不如集成解决方案中的激光调整电阻匹配那么好,这可以从图4中输出电压与CMV的关系曲线看出来。   图4. 输出电压与共模电压的关系——AD8271与分立解决方案比较   假设使用理想运算放大器,则CMRR为:   其中,d为差动放大器的增益,t为电阻容差。因此,对于单位增益和1%电阻,CMRR为50V/V或约34dB;使用0.1%电阻时,CMRR增加到54dB。即使采用具有无限大共模抑制的理想运算放大器,整体CMRR也会受电阻匹配的限制。某些低成本运算放大器具有60 dB至70 dB的最小CMRR,使误差更为糟糕。   低容差电阻   放大器在其指定工作温度范围内通常表现良好,但必须考虑外部分立电阻的温度系数。对于带有集成电阻的放大器,电阻可以进行漂移调整和匹配。布局通常使电阻相互靠近,因此它们会一同漂移,从而降低其失调温度系数。在分立情况下,电阻在PCB上散开,匹配情况也不如集成方案,产生的失调温度系数会更差,如图5所示。   图5. 系统失调与温度的关系——AD8271与分立解决方案比较   无论是分立式或是单芯片,四电阻差动放大器的使用都非常广泛。由于只有一个器件放置在PCB上,而不是多个分立元件,因此可以更快速、更高效地构建电路板,并节省大量面积。   为了获得稳定且值得投入生产的设计,应仔细考虑噪声增益、输入电压范围和CMR(达到80dB或更高)。这些电阻均采用相同的低漂移薄膜材料制成,因此在一定温度范围内可提供出色的比例匹配。

  • 发表了主题帖: IC 放大器的那些问题

         就像我们为系统功耗、接地及信号回路找到合适配置时,往往会引入一些干扰。在理解IC放大器的“去耦”、“接地”概念时也常常会被一些显而易见的问题所愚弄。   下面为大家阐述一个一般性原则,之后我们再慢慢讨论与集成电路放大器相关的去耦与接地问题——   首先请思考:电流流向何处?   表面来看,这是一个显而易见的问题。但提到电流时,人们一般都会想到电流从某个地方“流出”,然后“流过”其他地方,却忽视了电流如何流回源点的问题。在实际操作中,人们似乎认为所有“接地”或“电源电压”点都是相等的。但忽略了一个事实 :这些点构成电流在其中流动并产生有限电压,它们是导体网络的一部分。   如果要进行前瞻性规划,我们必须得考虑电流的起点及返回点,必须确定结果产生的电压降的作用。而这又要求对去耦及接地电路的原理有一定的了解。然而在设计采用了集成电路时,这样的信息往往无从获取与难以理解。   我们的IC放大器是非常常用的线性IC之一,但幸运的是:就功率及接地问题而言,多数运算放大器都可归入少数类别。尽管系统配置可能带来令人生畏的去耦及信号回路问题,但通过了解运算放大器,我们可以找到解决更多此类问题的基本方法。   运算放大器有四个引脚   一般的读者在看过任何一本运算放大器的课本之后,可以都会认为:理想的运算放大器应该有三个引脚——一对差分输入引脚和一个输出引脚。如下图所示:   图1:常规“三端”运算放大器   但如果你真的有了解基本原理,那么必然能看出事实并非如此。如果放大器有一个输出电压,那它必然以某个点为参考进行测量。从理想运算放大器来看,它拥有无限的共模抑制性能,因而可以排除输入引脚作为参考点的可能。如此来看,必然还存在着第四个引脚。换个角度来看,如果放大器需要向负载提供输出电流,则该电流必须从某个地方进入放大器。而理想情况来看,输入电流不流动,这样一来结论仍然是“需要第四个引脚”。   一种常见的做法是在图中指出第四个引脚为“接地”端。我们不讨论什么是“接地”端,多数集成电路运算放大器(包括很多模块化运算放大器)并不存在“接地”端。对于这些电路 ,第四个引脚是电源引脚中的一个或两个。这种情况 下,人们倾向于将两个电源电压与接地归在一起。电源线路确实会在放大哭带宽范围内,在所有频率下产生较低的阻抗。然而,当阻抗要求未得到满足时,众多问题就会随之而来,包括噪声、瞬态响应差,振荡等问题。   差分至单端转换   简单运算放大器的基本要求之一是“输入端加载的全差分信号必须转换成单端输出信号”。单端指的是相对于经常被忽视的第四个引脚而言的。这可能使问题复杂化。如下图2所示:   图2:简化版“真实”运算放大器   上图所示信号流用于多种流行的集成电路系列中。虽然细节不尽相同,但基本信号路径与101、741、748、777、4136、503、515等集成电路运算放大器大致相同。电路首先将差分输入电压转换成差分电流。该输入级函数在图2中表示为PNP晶体管。然后通过与负供电轨相连的电流镜,将电流从差分转换成单端形式。电流镜像输出驱动差一个电压放大器以及作为积分器连接的功率输出级。该积分器控制着开环响应,其电容既可外加亦可内置。关于上面这种简化型号的说明大多都没有突出积分器拥有一个差分输入的事实 。由几个基极发射极电压提供正偏置,同相积分器输入则以负电源作为参考 。   显然,放大器输出与负电源之间的大部分电压差会出现在整个补偿电容中。如果负电源电压突然发生变化,积分器放大器将强制输出随之而变。当整个放大器处于闭环配置时,其输入端产生的误差信号将深度恢复输出 ,但恢复程度受限于放大哭喊的压摆率。结果就是,这类放大器可能拥有出色的低频电源抑制性能,但负电源抑制却存在较大限制。由于导致输出恢复的是流程输入端的反馈信号,因此,对于频率输出恢复的是流程输入端的反馈信号。因此,对于频率超过闭环带宽的信号,负电源抑制比将接近0。即:高速高电平电路可以通过负电源线的公共阻抗与低电平电路“通信”。   此类放大器的问题与负电源端相关。虽然正电源抑制比也可能因频率增加而下降,但其影响程度较轻。一般而言,正电源上的小瞬变只会对信号输出产出轻微影响。这些灵敏度之间的差异可能使放大器瞬态响应出现明显不对称现象。如果驱动放大器的目的是在其额定负载范围内产生正电压摆幅,则放大器将从正电源吸取电流脉冲。   这样的脉冲可能导致电源电源瞬变,但正电源抑制将最大程度地降低对放大器输出信号的。在与此相对的情况下,负输出信号将从负电源中抽取电流。如果脉冲在总线上导致“毛刺”,则欠佳的负电源抑制性能将在放大器输出端带来类似的“毛刺”。虽然正脉冲测试可以得到放大器瞬态响应,但负脉冲测试实际上可以助您更好地了解电源负轨瞬态响应,而不是放大器响应!   事实上,电源脉冲响应本身并不是放大器上可能出现的东西。30或40厘米的电线可以充当一下高Q电厂,从而给阻尼通常过高的电源响应增加高频成分。在放大器附近安装一个去耦电容也不一定能解决问题,因为电源必须在某个地方去耦。如果去耦电流注过较长路径 ,仍有可能产生不良毛刺。   图3所示为负电源去耦的三种可能配置。在3a中,虚线表示通过去耦线路及接地线路的负信号电流路径。如果负载“接地”及去耦“接地”在电源处相接,则接地线路上的毛刺类似于负电源总线上的毛刺。根据反馈及信号源的“接地”方式,去耦电容导致的有效干扰可能大于电容的设计抗干扰能力。   图3a:无效负电源去耦   图3b展示了如何利用去耦电容降低V形及接地总线的干扰。负载电流中的高频成分被限制在一个不含接地路径的环路中。如果电容的容量够大、质量符合要求,则可降低负电源上的毛刺而不干扰输入或输出信号 路径 。   图3b:针对“接地”负载优化的去耦负电源   如果负载情况复杂(如图3c),则需要进行更多的思考。如放大器驱动的是流向虚拟地的负载,则实际负载电流不会返回接地。相反,该电源必然由形成虚拟地的放大器提供。   图3c:针对 “虚拟地”负载优化的去耦负电源   在这种情况下,如果将第一放大器的负电源去耦至第二放大器的正电源,则会闭合快速信号电流环路而不干扰接地路径或信号路径 。当然,为了避免干扰输出基准电压源,必须为第二放大器提供从接地至V形总线的低阻抗路径。理解去耦电路的关键在于认清实际负载和信号电流的去向。而优化电路的关键是在接地等信号路径旁路这些电流。   考虑整个电路时,通常会出现冲突。例如,多个放大器可能由同一电源驱动,而每个放大器又需要独立的去耦电容。总体而言,去耦电容全部呈并联 状态。然而,事实 上,互连电源的电厂及接地线路 会将这种看似无碍的配置转换成一个复杂的 L-C 网络。在处理快速信号波阵面的电路中,通过数厘米线缆并联的去耦网络通常意味着麻烦。   图4:并联去耦谐 振阻尼   图4展示了通过小电阻来降低 不良谐振电路 Q 值的方法。一般情况下,这些电阻是可以容许的,因为它们在运算放大器电源端将不良高频叮当声转换成小阻尼信号。虽然剩余信号具有较多的低频成分,但可以通过运算放大器的电源抑制性能而予以处理。

  • 发表了主题帖: 射频模块的ESD该如何考虑和设计?

          硬件工程师在设计产品时,ESD抗扰度是一个重要的考虑指标。静电对于大部分电子产品来说都存在危害,射频模块对静电更加敏感。那么针对射频模块类产品,ESD抗扰度应当如何考虑和设计呢?   关于ESD抗扰度等级,不同产品不同行业对应着不同的标准,国际电工委员会所颁布的IEC61000-4-2标准适合于各种电气与电子设备做电磁兼容性的测试。在进行产品设计前需要先规定好产品的ESD抗扰度的等级,要么根据标准来定义要么根据产品实际需要来定义。这样才可以有依据的进行产品设计及测试。     关于ESD抗扰度等级的实现方法,主要有外壳设计、硬件设计及PCB布局、元器件选型、软件修复等。其中在硬件设计方面,一个重要的方法是在输入或输出的关键电路节点处加入ESD保护器件。ESD保护器件一般使用瞬变电压抑制器,也就是硬件工程师常说的TVS(Transient Voltage Suppressor)二极管,TVS管的关键特性是它在正常工作电压下具有高阻抗,而在电压超出正常工作电压的时候变成了低阻抗,此时将电流直接从敏感部件引导至系统地或者大地(外壳地),抑制TVS管两极间受到的反向瞬态高电压,对于静电,适合选用双向TVS,而不是单向TVS。    对于IOT行业,无线产品是其重要的组成部分,而无线模块又是无线产品的重要一环,本文主要对无线模块天线端及射频接口的ESD设计进行说明,无线模块产品在设计上可以加上ESD保护器件,而应用无线模块的底板在设计时则需要在天线接口处考虑可靠有效的静电泄放路径,保证静电泄放路径不经过敏感器件及敏感线路。   在使用无线模块的时候,从无线模块产品手册上有时会看到天线端需避免静电直接打到天线的射频接口,甚至用手去拿模块最好都要带上防静电手套。这就说明无线模块的射频输出端是对静电敏感的,超过一定的静电等级可能会使得无线模块受到永久性的损坏。此处推荐一种无线模块射频输出端的ESD设计电路结构,如图3所示。   图3 无线模块射频输出端ESD设计   使用TVS管虽然可以提高无线模块天线端的ESD性能,但是有一个隐患是可能会产生谐波噪声而使得无线模块的接收灵敏度降低。这对TVS管的选型提出了要求,优选TVS管可以避免这一隐患,所以在器件选型的要关注谐波噪声这一关键指标。   对于外接天线版本的无线模块,一般需要在应用无线模块的底板上放置天线接口(比如SMA接口),以便安装所需的天线。这时候需要充分考虑静电在底板的泄放路径,保证静电不要通过射频转接线进入到无线模块产品本身,保证即使有静电导入天线接口,静电也可以快速的通过我们所设计的泄放路径泄放到大地,这样可以有效提高产品的可靠性。一般情况下,推荐使用如图4所示的阻容网络来传导静电,注意在PCB布局时保证静电泄放路径最短且不经过静电敏感器件、射频电路以及敏感线路(比如数据线、时钟线等),此处的电容需是耐高压的器件。   图4 静电泄放阻容网络   下面通过一个ESD设计案例来说明这一思路。   如图5所示,J12是4G无线模块的SMA天线接口,且裸露在空气中,所以静电就有可能从SMA引入到PCB的地。为了避免静电导入4G无线模块,使得模块损坏,就需要在PCB上设计静电泄放阻容网络,设计原则是该静电泄放路径最短且不经过静电敏感网络,必要时可以挖槽增加隔离,或者在静电泄放路径上打过孔来减小电阻使得静电可以更多、更快的从设计的路径上泄放。   下图中PCB的螺丝孔通过螺丝固定在外壳上,外壳是泄放静电的主要载体,所以此处的做法就是在SMA与螺丝孔(即外壳)之间增加静电泄放阻容网络R36并联C82,SMA、静电泄放网络与螺丝孔在一条直线上,保证路径最短,且路径上没有任何走线,在静电泄放路径上特意增加一些过孔来减小阻抗。最终做出来的样机也通过了ESD抗扰度实验。

  • 发表了主题帖: 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-07-16
  • 发表了主题帖: msp430f5529中断笔记

    定义中断服务程序 #pragma vector=PORT1_VECTOR    //P1口中断向量 __interrupt void Port_1(void)            //声明中断服务程序,名为Port_1 { ...                                                    //中断服务程序 } 定时器中断应用例程 #include <msp430.h> void delay(unsigned int i) {     volatile unsigned int j;     for(;i>0;i--)         for(j=0;j<2000;j++);                   // 延时 } int main(void) {   volatile unsigned int i ;   WDTCTL = WDTPW+WDTHOLD;                   // 关闭看门狗   P1DIR |= BIT0;                            // P1.0 输出   P4DIR |= BIT7;                            // P4.7 输出   TA0CCTL0 = CCIE;                          // CCR0中断使能   TA0CCR0 = 50000;   TA0CTL = TASSEL_2 + MC_1 +TACLR;          // SMCLK,增计数模式,清除TAR   __bis_SR_register(LPM0_bits+GIE);          // 进入低功耗模式0,使能中断 } #pragma vector=TIMER0_A0_VECTOR __interrupt void TIMER0_A0_ISR(void)   {     P1OUT ^= BIT0;                          // 反转 P1.0状态     P4OUT ^= BIT7;                          // 反转 P4.7状态     delay(50);   }  

  • 发表了主题帖: MSP430F5529 生成PWM波 with CCS

    大概就是通过时钟来产生某个确定频率的PWM波 用FPGA可以得到更完美的波形,不过如果只是提供一个CLK波的话F5529LP就完全可以做到 #include <msp430.h>   unsigned int temp;   int main(void) {       WDTCTL = WDTPW | WDTHOLD;                      //关闭看门狗       //Initialize     P1DIR |= BIT0;       P1OUT |= BIT0;     P1SEL |= BIT2;                                             //从P1.2输出     P1DIR |= BIT2;       //初始化定时器     TA0CTL |= TASSEL_2 + ID_0 ;     TA0CCTL1 = OUTMOD_7;      TA0CCR0 = /*Period*/; //Change me     TA0CCR1 = /*Duty*/;   //Change me       __delay_cycles(2000);     __bis_SR_register(LPM4_bits + GIE);     return 0; }  

  • 发表了主题帖: MSP430F5529库函数

         大学期间写的MSP430F5529库函数,期间断断续续写了快一年,从刚开始的1.0版本到最后的2.+版本,最后才有了让笔者满意的库函数。      MSP430是一款中低端的单片机,以至于很多人认为它不需要库函数,选择使用手写代码开发,然而,笔者却不这么认为,在库函数完成后,笔者使用该单片机开发时相比之前的速度提升了很多倍,当然一个不可忽略的原因是我更熟悉这单片机了。      库函数几乎包括了单片机上所有的外设模块,除此之外,笔者还提供了常用的OLED,LCD,LED,KEY,NRF24L01等模块的驱动库。      遗憾的是,笔者并没有精力写出使用手册,仅给出了部分常用外设的使用例子,从这些例子中可以看出使用库函数的方便。部分刚入门的可能对库函素本身就不熟悉,使用上可能存在困难,笔者建议可以先参考山外的库函数使用手册。     在开发库函数的过程中,笔者运用了很多C语言中一些极少用到的知识点,比如位域,可变参数,宏定义等,使得库函数使用起来十分灵活。开发这个库函数的过程中,笔者对c语言,单片机都有了更深入的认识,这应该是最大的收获了。  

  • 发表了主题帖: MCU驱动GEC6818程序开发记录

    在GEC6818开发平台上可以显示JPG格式的图片 一. 任务:显示RGB颜色LCD液晶屏幕中   --> 例如: 填充紫色到LCD上。 1. 分析技术点 1)了解LCD液晶屏幕组成架构? 2)在嵌入式平台如何处理LCD屏幕? 3)如何在工程访问LCD液晶屏? 4)如何写入数据到LCD屏幕中?数据是什么?该如何表示颜色? 二. 研究LCD液晶屏幕参数 1. 什么是分辨率?GEC6818平台LCD屏幕分辨率是多少? 分辨率指的是设备总像素点数是多少,例如:电视机1080*720 -> 一共有1080*720这么多个像素点 GEC6818平台分辨率->800*480 2. 什么是像素点? 像素,就是图像元素,指的是由RGB颜色组成一个点。 3. 究竟每个像素点如何组成? 像素点占多少个字节数?  --> 通过在GEC6818平台输入命令知道 [root@GEC6818 /]#cat /sys/class/graphics/fb0/bits_per_pixel  32 ---> 1个像素点  -->  32位 已知:1个字节 = 8位 ---> 1个像素点  --> 4个字节 结论: 每一个像素点  --> 透明度,红色,绿色,蓝色 三. 在嵌入式平台如何处理LCD屏幕? 原理:在linux下,一切都是文件。  --->在linux下,LCD液晶屏是被看作是一个文件。 LCD液晶屏幕对应的文件名是什么? --->规律:嵌入式平台硬件设备文件名存放dev目录  dev->device 1. 切换到/dev目录下 [root@GEC6818 /]#cd /dev/ [root@GEC6818 /dev]# 2. 查看当前目录下所有的文件名是什么? [root@GEC6818 /dev]#ls fb0  --> 就是LCD液晶屏幕的文件名 四. 如何在工程访问/关闭LCD液晶屏?  --> 新的函数:open 1. 在Ubuntu中,输入命令查看open函数用法 gec@ubuntu:~$ man 2 open   --> 结束查询->按‘q’ 头文件是什么? #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> 函数原型是什么? int open(const char *pathname, int flags); 参数填什么? pathname:需要打开的文件的路径名  -> 文件在哪里? flags:操作文件的权限 O_RDONLY  --> read only   -> 只读 O_WRONLY  --> write only  -> 只写 O_RDWR    --> read write  -> 可读可写 返回值是什么? 成功:new file descriptor  -> 新的文件描述符  --> 其实是大于等于0  >=0 失败:-1 2. 如何关闭? 在Ubuntu中,输入命令查看close函数使用方法: 头文件是什么? #include <unistd.h> 函数原型是什么? int close(int fd); 参数填什么? fd: -> file descriptor  -> 文件描述符 返回值是什么? 成功:0 失败:-1   练习: 写一个程序   --> LCD设备名字: /dev/fb0      1. 首先访问LCD屏幕             如果访问成功,则在终端打印open lcd ok!         如果访问失败,则在终端打印open lcd error!          2. 然后关闭LCD屏幕         如果关闭成功,则在终端打印close lcd ok!         如果关闭失败,则在终端打印close lcd error! 参考代码: ------------------------------------------ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main()  //->函数头 {            //->函数体,写一些可执行的内容     //->使用了printf才需要包含头文件     printf("this is first program!\n");      int lcd_fd;//关于LCD液晶屏幕的文件描述符     lcd_fd = open("/dev/fb0",O_RDWR);     if(lcd_fd >= 0)     {         printf("open lcd ok!\n");     }     else{         printf("open lcd error!\n");     }          int ret;     ret = close(lcd_fd);     if(ret == 0)     {         printf("close lcd ok!\n");     }     else{         printf("close lcd error!\n");     }          return 0; //->返回值  成功:0  失败:非0 } --------------------------------------- 五. 如何写入数据到LCD屏幕中?数据是什么?该如何表示颜色? 1. 如何写入? --> 新函数:write()  在Ubuntu中输入查询命令: gec@ubuntu:/mnt/hgfs/zdnf/02$ man 2 write 头文件是什么? #include <unistd.h> 函数原型是什么? ssize_t write(int fd, const void *buf, size_t count); 参数填什么? fd: -> file descriptor  -> 文件描述符 buf:-> buffer  --> 写入的数据的缓冲区  --> RGB颜色 count: --> 写入的总字节数 返回值是什么? 成功:已经写入的字节数 失败:-1 2. 数据是什么? 既然任务是填充RGB颜色到LCD屏幕,数据就应该RGB颜色。        write()     颜色 --------> LCD液晶 3. 该如何表示颜色? 结论: 每一个像素点  --> 透明度,红色,绿色,蓝色 例子:红色如何表示? 一个像素点=32位 00000000111111110000000000000000  --> 红色 0x00FF0000   --> 红色 0x00FF00FF   --> 紫色 0x00FFFF00   --> 黄色 ---------------------填充红色示例代码------------------------------- #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main()  //->函数头 {            //->函数体,写一些可执行的内容     //->使用了printf才需要包含头文件     printf("this is first program!\n");      int lcd_fd;//关于LCD液晶屏幕的文件描述符     lcd_fd = open("/dev/fb0",O_RDWR);     if(lcd_fd >= 0)     {         printf("open lcd ok!\n");     }     else{         printf("open lcd error!\n");     }          int color = 0x00FF0000;     int i;     for(i=0;i<800*480;i++)     {         write(lcd_fd,&color,4);     }          int ret;     ret = close(lcd_fd);     if(ret == 0)     {         printf("close lcd ok!\n");     }     else{         printf("close lcd error!\n");     }          return 0; //->返回值  成功:0  失败:非0 } -----------------------------------------------   练习:填充紫色到LCD屏幕。 一. 项目: 基于网络远程控制多媒体系统           socket通信    客户端   <------------>   服务器    UI界面            播放视频    触摸屏           控制视频状态 二. UI界面如何设计?JPG格式图形特点?JPG图片如何显示LCD液晶屏幕? 1. UI界面如何设计? 已知GEC6818平台LCD液晶屏幕分辨率->800*480  -->横:800个像素点  竖:480个像素点 想在LCD液晶上全屏显示JPG格式图片-> UI界面->分辨率:800*480 2. 根据项目要求设计UI界面 1)按钮内容 控制视频状态:快进、快退、音量+、音量-、暂停/继续、静音/恢复、退出。 2)按钮大小 -> 由于在客户端中触摸屏控制按钮,所以必须清楚知道按钮的范围! 画图软件: 重新调整大小->像素->把"保持纵横比"选项去掉 水平:800 垂直:480 3. 为什么选择使用jpg格式图片? 由于jpg格式文件经过压缩的,所以文件大小相对其它格式图片较小,方便开发时进行数据传输。 而且显示jpg图片编译移植jpg库,然后jpg源码调用库里面的接口就可以。 三. JPG图片如何显示LCD液晶屏幕? 任务: UI.jpg  --> 显示在GEC6818平台 首先拿到两个文件: jpeg_show.c  --> 显示jpg图片的源码 jpegsrc.v9a.tar.gz  --> jpg库 1. 在Ubuntu中,切换到源码所在的路径 gec@ubuntu:~$ cd /mnt/hgfs/zdnf/02/ 2. 尝试在Ubuntu Linux系统编译源码: gec@ubuntu:/mnt/hgfs/zdnf/02$ arm-linux-gcc jpeg_show.c -o jpeg_show 编译出错: error: jpeglib.h: No such file or directory  -> 找不到jpeglib.h这个头文件。 jpeglib.h->其实是存在于jpg库中,所以编译之前,首先要编译移植jpg库。 四. 编译移植jpg库? 1. 什么是编译移植? 其实就是安装jpg库的文件到Ubuntu linux系统中。 2. 如何编译移植? jpegsrc.v9a.tar.gz  --> jpg库: 开源免费 1)先把jpg库放置在共享目录下 2)在linux中,切换到jpg库所在的路径下 gec@ubuntu:~$ cd /mnt/hgfs/zdnf/02/ 3)在linux中解压jpg库,一般地解压到家目录下  --> 因为在家目录下,拥有一切权限。 gec@ubuntu:/mnt/hgfs/zdnf/02$ tar zxvf jpegsrc.v9a.tar.gz -C ~   tar: 解压/压缩的linux命令   z: 处理以.gz结尾的压缩包   x: 解压   c:压缩   v: 以可见的方式进行解压,这个参数不是必须。   f: 文件   -C: 指定解压之后的路径   ~: 家目录 如果有v选项,则会打印解压的过程 jpeg-9a/ jpeg-9a/wrppm.c jpeg-9a/wrrle.c jpeg-9a/maketdsp.vc6 jpeg-9a/jdinput.c 4)回到家目录下,看看有没有一个新的目录叫jpeg-9a/ gec@ubuntu:/mnt/hgfs/zdnf/02$ cd ~ gec@ubuntu:~$ ls   --> 看到jpeg-9a/目录 5)在家目录下创建一个新的目录,作为安装之后的目录,并给予权限 创建目录linux命令:mkdir  --> make diretcory gec@ubuntu:~$ mkdir jpgbuf   --> 创建目录 gec@ubuntu:~$ chmod 777 jpgbuf  --> 修改目录权限 为什么要修改目录权限? 因为在windows中,创建了一个新的文件夹,我们可以直接把文件丢进去。 但是在linux下,创建一个新的目录,是默认没有写的权限,我们不能丢文件进去,所以就没办法安装到目录中 --> 结论: 创建完目录后,通过chmod命令给予权限。 6)切换到解压之后的目录下 gec@ubuntu:~$ cd ~/jpeg-9a/ gec@ubuntu:~/jpeg-9a$ ls   --> 看到一个文件,叫configure  -> 配置文件。 7)配置 gec@ubuntu:~/jpeg-9a$ ./configure --host=arm-linux --prefix=/home/gec/jpgbuf 其中: ./configure  --> 执行当前目录下的configure文件 --host=arm-linux  --> 指定是交叉编译 --prefix=/home/gec/jpgbuf   -->指定安装路径 8)编译 gec@ubuntu:~/jpeg-9a$ make make  all-am make[1]: Entering directory `/home/gec/jpeg-9a' make[1]: Leaving directory `/home/gec/jpeg-9a'  --> 则需要清除链接文件 gec@ubuntu:~/jpeg-9a$ make clean  -> 清除链接文件 rm -f cjpeg djpeg jpegtran rdjpgcom wrjpgcom test -z "testout.ppm testout.bmp testout.jpg testoutp.ppm testoutp.jpg testoutt.jpg" || rm -f testout.ppm testout.bmp testout.jpg testoutp.ppm testoutp.jpg testoutt.jpg test -z "libjpeg.la" || rm -f libjpeg.la rm -f ./so_locations rm -rf .libs _libs rm -f *.o rm -f *.lo gec@ubuntu:~/jpeg-9a$ make   --> 重新编译   CC       jaricom.lo   CC       jcapimin.lo   CC       jcapistd.lo   --> 正在编译 9)安装 gec@ubuntu:~/jpeg-9a$ make install 安装结果:切换到jpgbuf路径看看 gec@ubuntu:~/jpeg-9a$ cd ~/jpgbuf gec@ubuntu:~/jpgbuf$ ls bin  include  lib  share   --> 如果看到有4个目录,则编译移植jpg库成功! 五. 分析jpg库编译移植之后文件有哪些? bin:   cjpeg  djpeg  jpegtran  rdjpgcom  wrjpgcom  --> 二进制文件 include: jconfig.h  jerror.h  jmorecfg.h  jpeglib.h   --> jpg库头文件 lib: libjpeg.a  libjpeg.la  libjpeg.so  libjpeg.so.9  libjpeg.so.9.1.0  --> 库文件 share: man  --> 帮助文档 六. 已经编译移植完,接下来让源码调用库里面的内容。 1. 切换到源码的位置 gec@ubuntu:~/jpgbuf/share$ cd /mnt/hgfs/zdnf/02/ 2. 重新编译 gec@ubuntu:/mnt/hgfs/zdnf/02$ arm-linux-gcc jpeg_show.c -o jpeg_show 编译出错:error: jpeglib.h: No such file or directory 为什么编译移植之后,还是找不到? 因为用户编译移植完jpg库之后,编译器是不知道已经编译完成了,所以我们需要告诉编译器,头文件在哪里。 如何告诉编译器,头文件在哪里? --> 使用编译器选项: -I 头文件的路径 gec@ubuntu:/mnt/hgfs/zdnf/02$ arm-linux-gcc jpeg_show.c -o jpeg_show -I /home/gec/jpgbuf/include 编译出错: /tmp/cc2O0oBN.o: In function `main': jpeg_show.c:(.text+0x440): undefined reference to `jpeg_std_error' jpeg_show.c:(.text+0x45c): undefined reference to `jpeg_CreateDecompress' jpeg_show.c:(.text+0x474): undefined reference to `jpeg_mem_src' 原因:找不到对应的函数的定义,函数的定义其实在库文件中,找不到函数的定义,其实就是找不到库文件,库文件就是lib中。 如何告诉编译器,库文件在哪里? --> 使用编译器选项: -L 库文件的路径 -l库的名字 gec@ubuntu:/mnt/hgfs/zdnf/02$ arm-linux-gcc jpeg_show.c -o jpeg_show -I /home/gec/jpgbuf/include -L /home/gec/jpgbuf/lib 编译出错: /tmp/cc2O0oBN.o: In function `main': jpeg_show.c:(.text+0x440): undefined reference to `jpeg_std_error' jpeg_show.c:(.text+0x45c): undefined reference to `jpeg_CreateDecompress' jpeg_show.c:(.text+0x474): undefined reference to `jpeg_mem_src' -l库的名字  --> -l后面是没有空格的 libjpeg.so.9.1.0  --> 库的名字是lib与.so中间的名字 --> -ljpeg gec@ubuntu:/mnt/hgfs/zdnf/02$ arm-linux-gcc jpeg_show.c -o jpeg_show -I /home/gec/jpgbuf/include -L /home/gec/jpgbuf/lib -ljpeg 编译通过,生成一个新的文件:jpeg_show    七. 烧写程序到开发板中 1. 烧写可执行程序,输入烧写命令 [root@GEC6818 /]#cd / [root@GEC6818 /]#rx jpeg_show C 2. 传输->发送Xmodom->选择jpeg_show文件->发送 3. 修改权限 [root@GEC6818 /]#chmod 777 jpeg_show 4. 执行 [root@GEC6818 /]#./jpeg_show  ./jpeg_show: error while loading shared libraries: libjpeg.so.9: cannot open shared object file: No such file or directory 解决方案:将缺少的库libjpeg.so.9下载到开发板的/lib中 5. 下载libjpeg.so.9到开发板中 [root@GEC6818 /]#cd /lib [root@GEC6818 /lib]#rx libjpeg.so.9 C 传输->发送Xmodom->选择libjpeg.so.9文件->发送 6. 回到根目录,再下载图片到开发板中 [root@GEC6818 /]#cd / [root@GEC6818 /]#rx UI.jpg C 传输->发送Xmodom->选择UI.jpg文件->发送  

  • 2019-07-14
  • 发表了主题帖: MSP430程序升级方式

    对MSP430系列单片机进行编程的方式有以下三种:利用JTAG接口,利用BSL固件和利用用户自定义的升级固件。由于利用自定义升级固件进行程序升级的方式比较灵活,并且用途广泛,本文将对它作重点介绍。 1. 利用JTAG接口 MSP430系列的单片机都集成了JTAG接口,该接口实现了遵循IEEESTD1149.1规定的测试访问端口状态机(TAP Controller)。它使用一个四线串行接口(TEST用于引脚较少的芯片)。数据或指令从TDI(测试数据输入)移入;串行数据从 TDO(测试数据输出)移出; TCK(测试时钟)作为时钟信号输入;TMS(测试模式选择)信号控制TAP 控制器的状态。利用该接口可以移入指令和数据,从而控制目标芯片的地址线和数据线,达到读写目标芯片FLASH和仿真调试的目的[1]。另外TI现在推出了新型的调试接口—SPY-BI-WIRE,它采用两线制,其中一根为数据线(双向),一根为时钟线。 利用该接口的优点是不需要设计额外的电路和程序,采用仿真器即可下载程序。缺点是一旦用户为了保证代码的安全,烧断了JTAG的熔丝,那么就永久性的破坏了该接口,也就不能再使用该接口了。 2.利用BSL接口 BSL是 Bootstrap Loader的缩写,中文名称是程序装载器。它实质是固化在芯片中的一段通信程序(占用0C00h-1000h的地址空间),利用它可实现对FLASH的擦除和读写。由于其是固化在芯片中的,因此不必担心其被更改或丢失。 该接口使用5根线:GND,TX(P1.1/P1.0),RX(P2.2/P1.1),RST,TCK(TEST),在RST和TCK(TEST)加特定的电平时序信号,即可启动BSL程序,从而实现与目标芯片的通信。通信的字符格式是8个数据位,一个停止位和一个偶校验位。起始波特率为9600bps(BSL 1.6版本可更改波特率到38400bps)。BSL协议要求首先接受一个80h字符用于同步时钟,然后发送应答字符90h。接着接受8个字符,并根据命令跳转到相应的处理例程。BSL程序的C语言描述如下 Void main() {  Byte B,bArray[8];   LOCKSTATE = LOCK;  InitPorts();  While(1)  {   B=ReceiveSyncByte();    SyncTimer(B);  SendAck(); For( I = 0 ; I < 8 ;i++) bArray = ReceiveByte(); Switch(bArray) {  0x12: //写FLASH   For(I = 0 ; i<bArray[7] ; i++)  {   B=ReceiveByte();  If(LOCKSTATE ==UNLOCK) WriteByte(B);  }  Break;  0x10://接收口令  Receive32Byte(); If(PassWordCorrect) LOCKSTATE = UNLOCK;   Break;   0x18..0xn:  If(PassWordCorrect) DoTask();    }   If(NoError) SendNak();  else    If(TxData) SendData();  else    SendAck();  } 其实现细节可能因版本不同而变化。若用户想利用它来实现程序升级,可参考文[2][3]。 利用BSL程序进行升级的优点是节省代码空间,用户不需要实现自己的升级固件,而且现在已经有很多现成的BSL升级工具;缺点是需预留BSL接口,并且需要现场接线。 3.利用自定义升级固件 MSP430系列单片机的FLASH存储器模块是一个可独立操作的物理存储单元。全部模块安排在同一个线性地址空间中,存储器被分为多个512字节的段(信息段大小为128/64字节)。各段可单独擦除,并且在正常工作电压下程序可对FLASH进行擦写操作,因此特别适合在线程序升级(in-system program)。 自定义升级固件就是在程序中内置一段用于升级应用程序的代码,即可利用现有通信接口进行远程代码的升级。其实现原理是在目标芯片中放置2段代码:一段为应用程序,一段为升级程序。两者的地址段不重叠,这样就可以利用升级程序擦除应用程序并写入新的代码。 3.1引导程序 复位后先进入引导程序,由它来决定进入升级程序或应用程序。引导程序的意义在于当应用程序不存在或错误时能直接进入升级程序,从而保证升级不成功可进行再次升级。 引导程序的描述如下 Void main() {  While(1)  {   If(ResetVectorValid()) Application();   Updata();  } } 其中的ResetVectorValid()函数用于检测应用程序是否存在或是否有效。实现可以是检测EnterApplication的入口地址是否合法,一种简单的实现是 #define ResetVectorValid() (ResetVector !=FFFF) 其中ResetVector为应用程序的入口地址,该地址通常放在一个固定的地址中,升级程序后修改该入口地址。Application() 为应用程序,它若正常执行不会返回 , 只有在接受到升级指令后才返回。可在Application()中使用Return语句进入升级程序。 Updata()为升级程序,其入口处必须加检测指令,以确认是正常进入升级程序。进入升级程序后,通信端应先发送擦除指令,擦除原有代码,然后发送升级代码更新FLASH。若是具有外部扩展存储器或用户程序较小,可先接收整个程序段,若校验正确再写入,这样可靠性会更高。 这里有个策略就是最先擦除包含ResetVector的块,最后写入ResetVector的值,这样可以尽量保证不会进入不完整的应用程序。 3.2应用程序的编写 应用程序的编写没有什么大的变化,需要在通信协议中加入自定义的一个升级命令,用于进入升级程序。另外需更改链接文件(*.XCL),指定应用程序的地址范围,如下以应用程序地址范围为2500-F7DC为例(用//注释掉的为默认的设置) // Code //-Z(CODE)CSTART=2500-FFDF //-Z(CODE)CODE=2500-FFDF -Z(CODE)CSTART=2500-F7DF -Z(CODE)CODE =2500-F7DF // Constant data //-Z(CONST)DATA16_C,DATA16_ID,DIFUNCT,CHECKSUM=2500-FFDF -Z(CONST)DATA16_C,DATA16_ID,DIFUNCT,CHECKSUM=2500-F7DF // Interrupt vectors //-Z(CONST)INTVEC=FFE0-FFFF -Z(CONST)INTVEC=F7E0-F7FF 修改完毕后将该文件添加到工程。编译后的代码即可作为升级代码。 3.3升级程序的编写 新建一个工程,按如上的方法将升级代码定位到与应用程序不重叠的区域,如F800-FFFF,此时不修改   -Z(CONST)INTVEC=FFE0-FFFF 在升级程序中将除复位中断外的所有中断映射到应用程序中,一种办法是嵌入汇编,采用汇编的定位指令ORG;或者写15个中断影射函数,如下 //重新映射中断向量地址  #pragma vector=0x0    __interrupt void intvec_0(void)  {   asm("br & 0F7E0h"); //假设F7E0中存放中断15的地址  } 另外也可以采用动态确定中断入口地址的方法,即将中断向量地址放入约定好的RAM中,如下  __no_init void (*intvec1[16])() @ 0x200; //定义指向函数指针的数组,用于映射新的中断向量 //重新映射中断向量地址 #pragma vector=0x0 // __interrupt void intvec_0(void)  {   asm("push R15");   asm("mov #0x200,R15");   asm("call @R15");   asm("pop R15");  } 然后在应用程序中进行中断向量的映射,如 intvec1[TIMERA0_VECTOR/2]=Timer_A_0; 即在TIMERA0中断时执行Timer_A_0()函数。这样做的优点是可以在运行时动态决定中断函数的入口,即如高级语言中的虚函数(Virtual Function)。 当这两个函数块编写完毕后就可以进行工程测试了。 3.4应用程序与升级程序同时完成 也许您还希望两个函数在一个工程里完成。这时除了需要修改链接文件外,还需要注意以下几点: (1)将升级程序的所有函数定位到升级程序空间,即在函数前面加定位指令 #pragma location="UPDATECODE" // UPDATECODE为升级程序所在段的名称 (2)修改函数返回调用的例程。当函数返回时会调用弹出寄存器的默认例程,而这些例程可能并不在升级程序的地址空间内。一种解决方法是利用编译环境生成的LST文件(汇编代码),逐个修改函数返回时调用的弹出寄存器例程,这样就可以保证两者代码独立。这样做的缺点是每次更改C语言代码后,就要重新修改汇编代码,比较繁琐。另一种方法是考虑到升级程序所做的就是接受和发送数据,一般不需要使用中断。这样就可以在升级函数前面加入__monitor 编译指令,指明该函数为原子操作。这类函数入口处先压入SR并禁止中断,返回时使用RETI返回,此时编译器并不调用例程弹出保存的寄存器,而是根据进栈情况逐个弹出寄存器。 (3)更改SWITCH语句。使用SWITCH语句时编译器也会产生默认例程调用,很难屏蔽掉,故只有将SWITCH修改为多个判断语句。

  • 发表了主题帖: msp430f149单片机控制步进电机C语言实例

    #include <msp430x14x.h>    typedef    unsigned int  uint;   typedef   unsigned char uchar;    #define   PWM   BIT2    void int_clk()    {        uchar i;        BCSCTL1&=~XT2OFF;  //打开XT振荡器        BCSCTL2|=SELM1+SELS;//MCLK 8M and SMCLK 1M          do        {            IFG1 &= ~OFIFG;                 //清除振荡错误标志             for(i = 0; i < 100; i++)          _NOP();           //延时等待            }        while ((IFG1 & OFIFG) != 0);    //如果标志为1继续循环等待        IFG1&=~OFIFG;    }    void int_pwm()    {       P1SEL|=PWM;//选择P12作为PWM输出      P1DIR|=PWM;        TACCR0=800;//PWM信号周期10KHz      TACCR1=400;//占空比1:1      TACCTL1=OUTMOD0+OUTMOD1+OUTMOD2; //输出模式选择      TACTL|=TASSEL1+MC0;     }    void main()    {      WDTCTL=WDTPW+WDTHOLD;//关看门狗      int_clk();  //初始化时钟      int_pwm();  //初始化PWM      while(1);//结束    } 本例程是以msp430f149单片机的PWM控制步进电机的转动,以P1.2口为PWM输出端口,PWM信号周期为10KHZ,占空比为1:1。  

  • 发表了主题帖: MSP430直流电机控制

    基于MSP430的直流电机控制,直流电机可以启动/停止,正反转,加减速。加减速使用PWM进行控制。 所有程序如下: /* 引脚连接:IN1-P1.5   IN2-P1.7    ENA-P1.2           key1-P2.0  key2-P2.1   key3-P2.2   key4-P2.5   key5-P2.4         */   #include "msp430.h"   #define uint unsigned int #define uchar unsigned char   #define key1 0x01 #define key2 0x02 #define key3 0x03 #define key4 0x04   void key_process_1(); void key_process_2(); void key_process_3(); void key_process_4(); void key_check(); void Init_PWM();   uchar key_value;    //定义全局变量,键值 uint a = 10000;         //初值50%占空比   void main() {     WDTCTL = WDTPW + WDTHOLD;            Init_PWM();          P2IE = 0x37;    //P2.0~P2.3 中断使能     P2IES = 0x37;    //P2.0~P2.3 下降沿触发中断     P2IFG = 0x0;    //P2.0~P2.2 中断标志位清除          _EINT();    //中断允许     while(1){                //正反转按键按下         if(P2IN & BIT4){             P1OUT &= ~(BIT4 + BIT7);    //正转             P1OUT |= BIT0 + BIT5;         }         else{             P1OUT &= ~(BIT0 + BIT5);    //反转             P1OUT |= BIT4 + BIT7;                     }         CCR1 = a;         key_check();         switch(key_value){    //对键值进行处理             case key1:key_process_1();    break;             case key2:key_process_2();    break;             case key3:key_process_3();    break;             case key4:key_process_4();    break;             default:break;                     }         key_value = 0x00;    //键值清除         P2IE = 0x37;    //P2.0~P2.3 中断使能         P2IFG = 0x0;    //P2.0~P2.2 中断标志位清除     } }   #pragma vector=PORT2_VECTOR __interrupt void Port_2()    //P2中断服务程序 {     switch(P2IFG){    //对键值进行处理         case 0x01:key_value = 0x01;     break;         case 0x02:key_value = 0x02;     break;         case 0x04:key_value = 0x03;     break;         case 0x20:key_value = 0x04;     break;         default:P2IFG = 0x0;      break;     }     P2IFG = 0x0; }   void key_check() {     uint i;     for(i = 0;i < 200;i++);    //延时去抖动     if(0xff != (P2IN & 0xd8)){        //是否有键存在         while(0xff != (P2IN | 0xd8));    //一直等待按键松开     }     else    key_value = 0x00;    //延时去抖动无键按下,则清除键变量         } void Init_PWM() {       BCSCTL2 |= SELS ;                 //SMCLK = XT2       BCSCTL2 = DIVS0 + DIVS1;           P1DIR = 0xff;               // P1输出       P1OUT &= ~BIT2;       CCR0 = 20000;                // PWM 周期       CCTL1 = OUTMOD_7;            // CCR1复位/置位       CCR1 = a;                // CCR1 PWM 占空比50%       TACTL = TASSEL_2 + MC_1;     // 定时器A时钟源为SMCLK,增计数模式 } void key_process_1()    //启动 {     P1SEL |= 0x04;    //P1.2第二功能 TA1 输出PWM     TACTL= TASSEL_2 + MC_1;    //定时器A时钟源为SMCLK,增计数模式 } void key_process_2()    //停止 {     P1SEL = 0x0;    //P1.2第二功能 TA1 输出PWM     TACTL= MC_0;         P1OUT &=  ~BIT2; } void key_process_3()    //加速 {     a += 1000;     if(a >= 20000)    a = 20000; } void key_process_4()    //减速 {     a -= 1000;     if(a <= 0)    a = 0; }  

  • 2019-07-12
  • 发表了主题帖: C6000与C2000系列DSP之间串行数据通讯的研究与实现

            TMS320C6711是TI公司TMS320C6000系列32位浮点DSP,它具有专用硬件逻辑的CPU、片内存储器、片内外设,支持汇编和C语言的单独或混合编程。该系列DSP最主要的特点是采用了VLIW体系结构,因此可以单周期发射多条指令,实现很高的指令级并行效率。其计算和处理速度非常快,系统单指令周期可达到6.67ns,被广泛用于DSL、无线基站、雷达声纳、数字图像处理等方面。在TMS320C6711中有2个多通道缓冲串行接口McBSP,McBSP不仅可以配制成串行接口,还可以独立配制成通用的输入(GPI)、输出(GPO)和输入输出端口(GPIO)。其优点是数据处理能力强大,但控制接口少,片内集成外部设备少,控制能力较弱。         TI公司的TMS320LF2407为16位定点DSP微控制器,内嵌有看门狗定时器(WDT)、CAN总线控制器、模数转换器(ADC)、串行外设接口(SPI)、异步串行口(SCI)等多种外设模块,并有大量输入输出引脚(GPIO),可以满足控制系统多方面的控制需求。但由于TMS320LF2407的指令周期最短为25ns(40MHz主频),对于数据处理运算量特别大的系统,其运算速度略显不足。        多数数字图像处理应用系统既要求系统有强大的数据处理能力,以满足对图像处理的实时性要求,又要求系统有强大的控制能力,以便实现对外部众多设备的控制。在实际应用系统中,将TMS320C6711作为处理器完成数字图像的实时处理,TMS320LF2407作为控制器完成系统的控制功能,便可兼顾系统的数据处理能力与外部设备控制能力。这样的系统要求在TMS320C6711和TMS320LF2407之间建立有效的数据交换通道,本文所介绍的设计思想就是基于以上工程需要提出的。   1 系统结构         本系统为嵌入式数字图像处理系统,数字图像数据由下位机TMS320C6711处理,处理结果采用异步串口通讯的方式传送给上位机TMS320LF2407,TMS320LF2407将采集到的模拟量、开关量等参数,与通过异步串口接收到的图像处理结果一起通过CAN总线向远程监控终端发送。        TMS320C6711的多通道缓冲串行接口McBSP与TMS320LF2407的SCI模块,通过特定的软硬件设计可以支持使用标准格式的异步数字通讯。通讯数据的格式为:一个起始位、数据(长度可通过编程在16位~8位内可选)位、可供选择的奇/偶/非极性位、一个或两个停止位。 1.1 McBSP接口   McBSP可以分为数据通道和控制通道两部分。数据发送引脚(DX)和数据接收引脚(DR)分别负责数据的发送和接收,发送时钟引脚(CLKX)、接收时钟引脚(CLKR)、发送帧同步引脚(FSX)和接收帧同步引脚(FSR)提供串行时钟和控制信号。CPU和DMA控制器通过外设总线与McBSP进行通讯。当发送数据时,CPU和DMA将数据写入数据发送寄存器(DXR1,DXR2),接着复制到发送移位寄存器(XSR1,XSR2),通过发送移位寄存器输出至DX引脚。同样,当接收数据时,DR引脚上接收到的数据先移位到接收移位寄存器(RSR1,RSR2),接着复制到接收缓冲寄存器(RBR1,RBR2),RBR再将数据复制到数据接收寄存器(DRR1,DRR2)中,并通过串口事件通知CPU或DMA读取数据。这种多极缓冲方式使得片内数据通讯和串行数据通讯能够同时进行。 1.2 SCI接口   SCI模块支持CPU和其他使用标准格式的异步设备间的通讯。它具有SCIRXD(串行数据接收端)和SCITXD(串行数据发送端)两个I/O引脚。在全双工模式下具有一个发送器(包括SCITX2BUF及其主寄存器TXSHF)、一个接收器(包括SCIRXBUF及其RXSHF)。发送器在SCITXBUF存放要发送的数据,并每次一位地将数据移位至SCITXD引脚;接收时则每次一位地将SCIRXD引脚上的数据移入,载入SCIRXBUF和SCIRXEMU给CPU读取。具有一个可编程的波特率发生器,可得到超过65 000种不同的可编程速率。SCI为接收器和发送器提供独立的中断请求和中断向量:如果RX/BKINT ENA位(SCICTL2.1)被置位,当SCI接收到一个完整的帧,并把RXSHF中的数据传送到SCIRXBUF时,这个动作置位RXRDY标志(SCIRXST.6)并启动一个中断。如果TX INT ENA位(SCICTL2.0)被置位,则在任何时候,只要SCITXBUF中的数据送到TXSHF,发送器中断就会被认定,表示CPU可以向SCITXBUF写。这个动作置位TXRDY标志位,并启动一个中断。   2 硬件实现   当TMS320C6711D与TMS320LF2407A进行标准异步串行通讯时,TMS320LF2407A的SCI接口可直接支持该通讯,只需将SCI接口通过内部特殊功能寄存器配置为串行接口模式即可。而TMS320C6711D的McBSP除通过内部特殊功能寄存器配置成串行接口外,在硬件设计上还应将DR和FSR短接,并与SCI的串行数据发送引脚(SCITXD)相连。这是由于标准异步串行通讯中数据线上既包含了帧同步信息,也包含了数据信息。SCI的串行数据接收引脚(SCIRXD)与McBSP的DX相连。   由于TMS320C6711D与TMS320LF2407A的接口电压均为3.3V,二者引脚可直接相连,不需要电平转换。   3 软件参数的配置与计算 3.1 针对TMS320C6711D (1)波特率的计算 TMS320C6711D内部生成的串行时钟由系统时钟频率SYSCLK和采样率发生寄存器决定。MCBSP的内部数据时钟频率即异步波特率由以下公式确定: MCBSP异步波特率=采样率发生寄存器输入时钟频率/(CLKGDV+1) 其中:CLKGDV=采样率发生寄存器输入时钟频率/McBSP异步波特率-1。 当采样率发生寄存器的CLKSM=1时,采样率发生寄存器输入时钟频率=CPU内核输入时钟/2; 当采样率发生寄存器的CLKSM=0时,采样率发生寄存器输入时钟频率=CPU内核输入时钟。 在本系统中,TMS320C6711D的内核输入时钟为150MHz,采样率发生寄存器的CLKSM位设置为1。若使McBSP异步通讯波特率为312.5kb/s,则依据以上公式,采样率发生寄存器的CLKGDV段参数应配置为239,即十六进制的0xEF。 (2)特殊功能寄存器的初始化 TMS320C6711D内部与McBSP配置相关的特殊功能寄存器主要有:串口控制寄存器(SPCR)、接收控制寄存器(RCR)、发送控制寄存器(XCR)、采样率发生器寄存器(SRGR)、多通道控制寄存器(MCR)、接收通道使能寄存器(RCER)、发送通道使能寄存器(XCER)、管脚控制寄存器(PCR)。 TMS320C6711的McBSP与TMS320LF2407的SCI模块通讯时,系统对McBSP的配置要求为:数据为单项帧;每帧一个数据单元;数据单元字长为8bit;时钟下降沿处接收数据;在上升沿处发送数据;帧同步信号高有效;帧同步有效后,在其后的第一个时钟周期启动该帧的传输,即数据延迟为0;异步通信波特率为312.5kb/s。 3.2 针对TMS320LF2407A (1)波特率的计算 TMS320LF2407A内部生成的串行时钟由系统时钟SYSCLK频率和波特率选择寄存器决定。串行通信接口使用16bit波特率选择寄存器,数据传输速度可以被编程为65000多种不同的方式。 不同通信模式下的串行通信接口异步波特率由下列方法决定: BRR=1~65 535时的串行通信接口异步波特率为: SCI异步波特率=SYSCLK/[(BRR+1)×8] 其中:BRR= SYSCLK/(SCI异步波特率×8)-1 BRR=0时的串行通信接口异步波特率为: SCI异步波特率=SYSCLK/16 这里BRR等于波特率选择寄存器的16位值。 在本系统中,TMS320LF2407A的系统输入时钟为25MHz,若使SCI异步通信波特率为312.5kb/s,则依据以上公式,波特率选择寄存器参数应配置为9,即十六进制的0x0009。 (2)特殊功能寄存器的初始化        TMS320LF2407的SCI模块的异步串行通讯配置包括管脚配置、通讯模式配置和中断配置三方面,相关的特殊功能寄存器包括复用控制寄存器(MCRA)、端口数据和方向控制寄存器(PADATDIR)、串行通讯接口控制寄存器1和2(SCICTL1和SCICTL2)、通讯控制寄存器(SCICCR)、波特率选择高字节寄存器和低字节寄存器(SCIHBAUD和SCILBAUD)、优先级控制寄存器(SCIPRI)、接收状态寄存器(SCIRXST)等。 4 软件设计 系统中TMS320C6711通过McBSP将图像处理结果采用主动发送的方式发送给TMS320LF2407,后者通过响应SCI中断的方式接收数据信息。 5 串行通讯初始化程序        TMS320C6711与TMS320LF2407进行串行通讯的软件设计难点是McBSP与SCI的初始化程序设计。初始化程序包括对芯片的管脚配置、通讯模式配置和中断配置等方面。以下分别给出TMS320C6711的McBSP模块和TMS320LF2407的SCI模块的初始化程序模块。 (1)TMS320C6711的McBSP模块初始化程序 void init_mcbsp0_master(void) { MCBSP_Config mcbspCfg0= { 0x00010001,//配置spcr寄存器 0x000D0000,//配置rcr寄存器 0x00040020,//配置xcr寄存器 0x200000ef,//配置srgr寄存器, //波特率设定为312.5kb/s 0x00000000,//配置mcr寄存器 0x00000000,//配置rcer寄存器 0x00000000,//配置xcer寄存器 0x00000b0c//配置pcr寄存器 };   hMcbsp0=MCBSP_open(MCBSP_DEV0, MCBSP_OPEN_RESET); //选用port 0 MCBSP_config(hMcbsp0,&mcbspCfg0);   IRQ_map(IRQ_EVT_RINT0,13);//映射接收中断0为 //13号中断 IRQ_reset(IRQ_EVT_RINT0); IRQ_enable(IRQ_EVT_RINT0);//开中断   MCBSP_enableRcv(hMcbsp0); MCBSP_enableSrgr(hMcbsp0);//Handle to SRGR MCBSP_enableFsync(hMcbsp0); } (2)TMS320LF2407的SCI模块初始化程序 void sci_INIT(void) { MCRA=MCRA|0x0003; //设置引脚SCITXD/IOPA0和 //SCIRXD/IOPA1为串行通讯功能 PADATDIR=0X0100; //设PA口为输入口 SCICTL1=0x13; //使能接收和发送 SCICTL2=0x02; //禁止发送中断,使能接收中断 SCICCR=0X07; //8位字长,1个停止位,空闲线多处 //理器模式,无奇偶校验 SCIHBAUD=0x00; //波特率设为312.5kb/s SCILBAUD=0x09; SCIPRI=0x00; //接收为高优先级中断 SCIRXST=SCIRXST&0xbf;//清除SCI接收中断标志 SCICTL1=0x33; //保存设置 }       利用TMS320C6711的McBSP和TMS320LF2407的SCI可以实现异步串行数据通讯,具有电路简单、设置灵活、数据传输速度快、性能可靠稳定等特点。在此基础上可成功构建主从式双DSP数据处理系统,有效解决单一DSP系统数据处理能力与控制能力难以兼顾的问题。本文所介绍的设计方案已在实际应用系统中采用,并经长时间运行检验。实践证明,此设计方案是一种有效的多DSP数据交换手段。

  • 发表了主题帖: 利用MicroPython设计嵌入式系统的优缺点

           C / C ++编程语言长期以来一直主导着嵌入式系统行业,很少有其他语言可以找到立足点。虽然Java和Ada等语言已经尝试过,但Python的流行程度不仅在不断提高,而且已经在基于Linux的应用程序中找到了很长一段时间。但是,用于微控制器的Python似乎从未像MicroPython那样适合该法案,MicroPython是一个设计用于在微控制器上运行的Python 3.0端口。随着该项目现已进入第六个年头,它已经越来越受欢迎和发展势头,使其成为您用来设计下一个产品的编程语言。   让我们来看看使用MicroPython的一些优缺点:   Python编程语言具有浅薄的学习曲线,这使得开发人员可以非常轻松地开始使用它。事实上,我遇到过学习Python的小学生!Python为开发人员提供了一种高级编程语言,可用于构建简单的脚本,或者可用于开发复杂的面向对象的体系结构,这些体系结构使用现代软件项目的所有最佳实践。与C相比,Python还提供了内置机制,用于创建线程,处理错误并轻松集成到测试工具中。   变革之风正在我们身上,MicroPython可能是您用来构建下一个产品的编程语言。在本课程中,我们将研究如何使用MicroPython开发产品。与会者将远离本课程,详细了解他们需要做什么才能在下一个产品中使用MicroPython。   这些语言特征的有趣之处在于它们使开发团队的每个成员都可以成为程序员!MicroPython提供了一系列库,可以控制低级微控制器功能,从而消除复杂性。例如,硬件工程师可以设计电路板,几乎不知道微控制器(或C)的工作原理,开发可以通过控制GPIO测试电路板的高级脚本,甚至可以与I2C器件通信。在C中开发I2C驱动程序可能相当复杂,但使用MicroPython只需要以下代码行来创建I2C对象并将数据发送到从属设备:   而已!处理I2C的所有基础工作都由MicroPython库处理,这大大简化了开发。(看看我在C中的一个I2C驱动程序显示了几千行代码,授予它是一个非常强大的实现,并提供了一些功能,一旦奠定了基础工作,就可以与总线交互)。   利用MicroPython设计嵌入式系统的优缺点   在考虑使用MicroPython进行产品开发时,开发人员需要考虑几个关键因素。首先,开发人员需要考虑如何保护他们的应用程序代码。MicroPython允许开发人员加载基于代码文本的Python脚本或将这些脚本编译为字节码并将它们放入a.mpy模块中。这些解决方案的问题在于,任何能够访问MicroPython文件系统的人都可以轻松获得应用程序代码,这很容易实现。字节码确实使它更难一点,但将字节代码转换回可读代码并不困难。开发人员需要考虑他们需要系统的安全性,并可能采取额外措施来保护知识产权。   接下来,开发人员需要考虑如果出现问题他们将如何恢复他们的系统。根据所选的微控制器,它们的应用程序代码可以在MCU内部,也可以在外部存储设备(如SD卡)上。我发现文件系统在电源循环或欠压条件下不是很强大。如果文件系统损坏,MicroPython将通过将默认映像复制回文件系统来恢复它。开发人员需要确保将其默认代码集成到其内核版本中,以便在出现问题时,至少将其恢复为出厂默认设置,然后可以从设备上可能存在的其他内存位置恢复任何更新。   最后,开发人员可能希望确保他们有多个内存设备可供选择。新的旗舰MicroPython开发板,pyboard D系列,包括两个独立的SPI内存设备,每个2 MB。一个用于存储应用程序代码,另一个用于存储数据或其他信息。生产系统应该做类似的事情,甚至保留固件备份副本,以便在出现问题时,可以恢复应用程序而不会给用户带来任何问题。   使用MicroPython进行产品设计听起来很有趣,有没有人真正用它来开发商业产品?据我所知,MicroPython已被用于多种产品中,毫无疑问,我不知道更多。例如,MicroPython已被用于小型卫星中,用于电子电源和数据采集系统(其中一些我亲自参与过)。在欧洲航天局也已经在更大的卫星系统使用MicroPython调查。MicroPython还用于流行的OpenMV模块,该模块允许开发人员创建机器视觉应用程序。该模块运行MicroPython,允许开发人员通过交互式IDE创建脚本,然后将开发人员的脚本下载到模块。开发人员甚至可以训练机器学习模型并将其转换为在OpenMV模块上运行!这表明MicroPython可用于运行不仅仅是简单的算法或切换一些I / O.   使用MicroPython构建嵌入式产品无法满足每个开发团队的需求,如C / C ++,但它为开发人员提供了一个有趣的快速原型设计或开发商业产品的解决方案。当然还存在一些挑战,例如保护基于MicroPython的系统并确保确定性行为。这些挑战可以通过适当的应用设计预先克服。MicroPython的易用性和Python的普及使得使用MicroPython构建嵌入式系统成为一个有趣的机会。

  • 发表了主题帖: 嵌入式软硬件系统的工作原理

          从产品的应用角度来说,嵌入式系统是控制、监视或者辅助设备机器和车间运行的装置。而众多不同专业的人士从各自不同的角度思考和定位嵌入式系统,所以目前对嵌入式系统的定义非常多。嵌入式系统按照目前业界和学术界对嵌入式系统的普遍看法,是以应用为中心,以计算机技术为基础,软硬件可裁剪,适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。   大多数嵌入式系统的硬件平台。它包括两部分:1、以通用处理器为中心的协议处理模块,用于网络控制协议的处理;2、以数字信号处理器(DSP)为中心的信号处理模块,用于调制、解调和数/模信号转换。   嵌入式系统的核心部件是各种类型的嵌入式处理器,目前据不完全统计,当前全世界嵌入式处理器的品种总量已经超过1000多种,流行体系结构有30几个系列。由于嵌入式系统设计的差异性极大,因此选择是多样化的。   嵌入式处理器的功耗、体积、成本、可靠性、速度、处理能力、电磁兼容性等均受到应用要求的制约。在选择处理器时要考虑的主要因素在于调查上市的CPU供应商、处理器的处理速度、技术指标、处理器的低工耗、处理器的软件支持工具、处理器是否内置调试工具、处理器供应商是否提供评估板等。   尽管嵌入式系统有着无比广阔的市场需求和发展前景,但嵌入式系统的发展多年来却经历了一个曲折和痛苦的历程。随着微处理器的产生,价格低廉、结构小巧的CPU和外设连接提供了稳定可靠的硬件架构,那么限制嵌入式系统发展的瓶颈就突出表现在了软件方面。   从运行平台来分,嵌入式软件可以分为:1、运行在开发平台上的软件:设计、开发、测试工具等2、运行在嵌入式系统上的软件:嵌入式操作系统、应用程序、驱动程序及部分开发工具   可用于嵌入式系统软件开发的操作系统很多,但选择一个适合的操作系统,关键是从操作系统提供那些开发工具、操作系统向硬件接口移植的难度、操作系统的内存要求、开发人员是否熟悉此操作系统及其提供的API、操作系统是否有提供硬件的驱动程序、操作系统是否具有可剪裁、操作系统的实时性等方面进行考虑。   嵌入式应用软件是实现嵌入式系统功能的关键,为了提高执行速度和系统可靠性,嵌入式软件一般都固化在存储器芯片或单片机本身中,而不是存储于磁盘等载体中,软件代码要求高质量、高可靠性和高实时性。   嵌入式系统的硬件和软件都必须量体裁衣,与通用型处理器相比,嵌入式处理器的最大不同是将大部分工作用在为特定用户群设计的系统中,它通常都具有低功耗、体积小、集成度高等特点,能够把很多任务集成在芯片内部,从而有利于嵌入式系统设计趋于小型化,使得移动能力大大增强。   嵌入式处理器的功耗、体积、成本、可靠性、速度、处理能力、电磁兼容性等均受到应用要求的制约。   嵌入式微处理器具备的特点:1、对实时多任务有很强的支持能力,嵌入式系统能完成多任务并且有较短的中断响应时间,从而使内部的代码和实时内核的执行时间减少到最低限度。   2、具有功能很强的存储区保护功能,由于嵌入式系统的软件结构已模块化,而为了避免在软件模块之间出现错误的交叉作用,需要设计强大的存储区保护功能,同时也有利于软件诊断。   3、可扩展的处理器结构,可扩展的处理器结构能最迅速地开发出满足应用的最高性能的嵌入式微处理器。

  • 发表了主题帖: ARM寄存器分析以及异常处理方法

    ARM 有7个基本工作模式   User : 非特权模式,大部分任务执行在这种模式   FIQ : 当一个高优先级(fast) 中断产生时将会进入这种模式   IRQ : 当一个低优先级(normal) 中断产生时将会进入这种模式   Supervisor :当复位或软中断指令执行时将会进入这种模式   Abort : 当存取异常时将会进入这种模式   Undef : 当执行未定义指令时会进入这种模式   System : 使用和User模式相同寄存器集的特权模式   注意:除User(用户模式)是Normal(普通模式)外,其他6种都是Privilege(特权模式)。 Privilege中除Sys模式外,其余5种为异常模式。 各种模式的切换,可以是程序员通过代码主动切换(通过写CPSR寄存器);也可以是CPU在某些情况下自动切换。 各种模式下权限和可以访问的寄存器不同。   非特权模式:User   特权模式:   异常模式:FIQ IRQ Svc Abort Udef   非异常模式:System  Monitor   ARM寄存器分析   ARM共有37个寄存器,都是32位长度 37个寄存器中30个为“通用”型,1个固定用作PC,一个固定用作CPSR,5个固定用作5种异常模式下的SPSR。   r13(sp)用作堆栈可以保存上下文,便于以后跳转回来能继续执行   r14(lr)用于存储返回地址,当我们返回原模式可以bl lr或者mov pc lr这样就实现了返回   r15(pc):程序指针,PC指向哪里,CPU就会执行哪条指令(所以程序跳转时就是把目标地址代码放到PC中)   cpsr:中各个bit位表明了cpu的某些状态信息,这些信息非常重要,和后面学到的汇编指令息息相关(譬如BLE指令中的E就和   CPSR中的Z标志位有关) cpsr中的I、F位和开中断、关中断有关 cpsr中的mode位(bit4~bit0共5位)决定了CPU的工作模式,   在uboot代码中会使用汇编进行设置。   spsr:用来保存cpsr   1.条件标志位   -N: Negative result from ALU   -Z: Zero result from ALU   -C: ALU operation Carried out (进位/借位)   -V: ALU operation overflowed (溢出)   * N=1时,说明运算的结果为负数,N=0时,说明运算的结果为正数或零。   * Z=1时,说明运算的结果为0,Z=0时,说明运算的结果为非0。   * C:   - 加法运算(包括CMP):当运算结果产生了进位时(无符号数溢出),C=1,否则为0。   - 减法运算(包括CMP):当运算结果产生了借位时(无符号数溢出),C=0,否则C=1。   - 对于包含移位操作的非加减指令操作时,C为移除值的最后一位。   - 对于其它的非加减指令,C的值通常不变。   * V:   - 对于加减法运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号位溢出   - 对于其它的非加减法指令,V的值通常不变。   2.Q位:   * 在ARM V5及以上的版本的E系列处理器中,Q标识位指示增强的DSP运算指令是否发生了溢出,在其它版本的处理器中,Q未定义。   3.J位:   * 仅ARM 5TE/J 架构支持   * J=1时,处理器处于Jazelle状态   跳转过程   异常向量表   所有的CPU都有异常向量表,这是CPU设计时就设定好的,是硬件决定的。 当异常发生时,CPU会自动动作(PC跳转到异常向量处处理异常,有时伴有一些辅助动作) 异常向量表是硬件向软件提供的处理异常的支持。   ***异常产生时   做好保护现场的工作:   (1)把cpsr保存到spsr中,设置适当的cpsr(改变处理器的ARM状态、改变处理器进入相应的异常模式、(视情况)改变中断禁止位禁止相应中断)   (2)保存返回地址到lr   (3)设置pc为相应的异常向量   实现跳转。   ***异常返回时   做好恢复现场工作:   (1)从spsr恢复cpsr   (2)从lr恢复pc   注意:这些操作必须在ARM状态执行   TIPS:   (1)异常向量表中除了FIQ中断都是4个字节,所以只够存放一段异常处理程序的代码的首地址   (2)FIQ中断为快速中断,其中一个特殊的地方就体现在他的异常向量表(Vector Table)地址在最后一个,所以它可以存放不止4个字节,这样的话FIQ中断可以不用只存放地址,而是把整个异常处理程序放进去,这样就少跳转了一次,加快了中断相应速度。

  • 发表了主题帖: 关于嵌入式处理器的在线调试方法

         在FPGA 设计中使用嵌入式处理器软核( 如MicroBlaze、PicoBlaze 等) 构成可编程片上系统( SystemOn Programmable Chip,SOPC) ,相比于ASIC 具有更好的可修改性和可维护性,得到了普遍的应用。由于ARM,MicroBlaze 等大型处理器内核具备调试接口,在与之对应的调试模块配合下,调试软件可以通过JTAG 接口实现: 执行到断点处挂起、单步执行、查看处理器内部状态、查看和修改Memory空间中的数据等基本的在线调试功能。上述基本的在线调试功  能对嵌入式系统的调试具有重要意义。      对于PicoBlaze 等占用资源少、设计开发简单的小巧型处理器,一般不具备调试接口,然而在SOPC系统设计中经常需要使用上述处理器。使用上述处理器时,由于没有调试接口,属于大型处理器的标准高效的调试机制不再能够使用,因此通过指令集仿真( ISS,Cycle - accurate Instruction Set SimulaTIon) 和利用仿真工具对含有处理器的系统进行软硬件协同仿真是确保设计正确性的重要途径。         然而在诸如接收机基带信号处理等系统的设计中,仿真所用的测试用例往往覆盖率不够,或者在发现故障以后很难构造出与之相应的测试用例。因此迫切需要使在线调试功能能够方便地扩展到一般的处理器上。 针对上述应用需求,这里提出的新调试方法通过引入一种通用的调试模块( Universal Debug Module,UDM) 可以使没有调试接口的处理器建立起标准的调试机制。该调试模块利用处理器的中断机制实现处理器响应断点( breakpoint) 的机制,利用基于双端口RAM 中一种巧妙的地址映射机制实现同时对多行代码设置断点的功能,并且能够方便地实现被调试系统和调试主机之间调试信息和命令的交互。UDM 还具有易于扩展的优点,当SOPC 系统中有多个处理器时可以 共用一个UDM。        对于没有调试接口的处理器目前主要是通过在软件和硬件设计中充分考虑可能的调试需求,再加上调试主机和被调试系统之间的通信机制来实现在线调试的。这种调试模式下,调试代码需要插入到正常程序中,将调试信息输出到调试主机,同时还能够接收调试主机发过来的命令做出各种响应。该方法的主要缺陷是针对不同的调试需求,要不断修改正常程序中的调试代码,导致标准化和通用性程度不高。此处的UDM 在不对处理器内核做修改的情况下即可使这类处理器建立起方便的调试机制,是一种不同于主流大型处理器实现在线调试的方法。        使用UDM 的调试系统,利用与FPGA 同在一块PCB 板上的ARM、DSP 等处理器作为辅助调试用的嵌入式处理器( 下文中简称为辅助处理器) ,简化了UDM 与调试主机之间的通信。通过辅助处理器的总线接口,UDM 中的各种控制和数据寄存器被直接映射到辅助处理器的Memory 空间。                 在辅助处理器开发工具的Memory 窗口直接进行数据读写操作,就可实现对UDM 的操控。由于在一块PCB 板上同时集成FPGA 和嵌入式处理器芯片是很常见的设计,因此这种通信方式适用的范围很广。 UDM 直接作为FPGA 外部辅助调试的嵌入式处理器的外设,如果在外部处理器总线挂接多个UDM 模块,就能实现同时对多个处理器进行调试。          UDM 通过产生调试中断( DeBug Interrupt,DI) 信号,使处理器响应中断并调用调试服务程序( DebugRouTIne,DR) 。UDM 通过监测处理器的取指令地址( InstrucTIon Address, IA) 产生DI 信号。PicoBlaze 在运行DR 时可通过其总线接口访问UDM,从而实现调试信息的输出和对调试命令的响应。 产生DI 时由于处理器会立即执行DR,从而中断正常的执行流程转为为调试服务,因此决定DI 产生的时机是实现断点机制的核心。DI 信号是通过监测处理器的取指令地址( InstrucTIon Address, IA)产生的。直接通过一个比较器将IA 与一个数据比较一次只能设置一个断点,为了解决此矛盾采用了如下方法: 在UDM 中用双口RAM 存储断点配置信息,使RAM 中的每1bit 与程序存储区的一个地址对应起来,数据为1 代表设置了断点,0 代表没有。          将输入的IA 进行地址变换后对RAM 存储区寻址,使得RAM 在一端输出一个正好代表输出的地址处是否设置了断点信息,再根据此数据就可生成正确的DI 信号。在双口RAM 的另外一端,断点设置情况可以方便地被修改。这样一来可以设置的断点个数变为主要受UDM 中双口RAM 容量限制了。          只需在DR 中保证处理器不对目标程序的内外部环境造成改变,就等效于实现了处理器的挂起功能。因此,需要将DR 和目标程序的执行环境隔离开来,这可以通过对编译器进行某些设置或强制的编码规范来实现。在处理器被挂起之后,DR 与外部调试主机通信,通过查询命令寄存器的方式响应调试主机发出的  各种调试命令。这些命令包括: 将有关的调试信息搬移到外部调试主机可以观察的缓存区中、修改Memory 空间中的数据、退出DR 使目标程序继续执行等。  由于DR 必须与目标程序使用相互隔离的资源并且小型处理器中代码容量,外部Memory空间大小等都比较受限,因此DR 的设计应该尽可能占用较少的端口数、通用寄存器数和代码总行数。

  • 2019-07-11
  • 发表了主题帖: MSP430常见问题之FLASH存储类

    Q1:用IAR Embedded Workbench for MSP430 通过JTAG往MSP430上写程序。为了知道片内程序的版本,必须读出Flash 中内容。什么工具软件可以通过JTAG口实现这个功能? A1:熔丝未烧断的话,做个空程序的项目,然后在C-SPY选项里选择保留未改变的区域,DEBUG后看MEMORY里的内容!熔丝烧断的话只能用编程器或BSL,如果加密了,只能通过BSL来读了,不过你要知道中断向量表的32个字节的内容,即密码。 Q2:MSP430F449中我想把一个整形的数A存入某地方掉电也不丢失,作为以后程序运行的参数。是放在flash里面吗?用代码怎么实现? A2:如果只是个巴字节的话就把它作为数组或者变量定义到FLASH就可以了。如: const unsigned char; Q3: 430里面Flash的主存储区和信息存储区有什么区别么?是不是程序是保存在主存储区里面的?那信息存储区是存什么信息的呢?存进去之后是不是随时能够读写出来呢? A3: Flash分主Flash和信息Flash。如你所说,Flash主存储区主要来存储程序。信息Flash就是保存数据用的,可以随时读取。不过从物理特性而言他们是一模一样的,就段的长度有区别。当然信息段你也可以用来存储程序使用。 Q4:在flash 单字节写的时候,如我写在D区,可以不用全部清D区吗?因为D区还有先前保存的数据. A4: 不可以。只能先读出然后全部擦除再重新写入,如果FLASH可以如您说的那么操作,那就是EEPROM了 Q5: 目前使用的单片机是5V供电,买的flash存储芯片3.3V供电,单片机和存储芯片的连接是怎么的呢?我查看一些相关资料,说是可以直接连接,但是直接连接有问题,请问如何连接??需要什么样的方法?? A5: 最好在FLASH与MCU之间接一个电平转换芯片, 或者用MOFET管转换一下电平。 Q6:请问擦写FLASH 选择频率时,有没有要特别注意的,如果我用8M的,不分频,这样可以吗? A6:要注意啊,擦写Flash的频率在250~470K(具体可以查查手册);必须对FCTL2进行设置,使频率在这个范围内。 Q7: 用jtag接口往430 中写程序会不会破坏原有flash信息段保存的数据啊? A7:取消擦除信息段选项,即在IAR project 的options 选项中DEBUG 下的选项。 Q8:从数据手册上来看,在写的过程中,好像要求判断BUSY位来决定接下来的操作,可是为什么TI的例程里面没有判断BUSY bit ?是不是MCU会自动判断? A8:430对FLASH的写操作多种。比如块写(××OCKWRITE)和字节/字写(BYTE/WORDWRITE),在不同模式下,允许写的操作也不同。比如在字节写(BYTE WRITE)操作下,允许在FLASH MEMORY或在RAM下进行。当在FLASH MEMORY下进行写操作时,此时CPU是挂起的,直到写操作完成它才能执行下一个指令操作,但是当擦除程序是在RAM内初始化的,那么CPU就可以一直执行,如果不判断BUSY位的话,当RAM内代码执行完毕(us级),PC跳转到FLASH,而此时FLASH还再做擦除操作(ms级)就会造成非法访问,产生不可屏蔽中断。 Q9:MSP430的编译器默认是将数组定义在RAM 里面。请问如果我要将定义的数组直接保存在flash里面应该如何定义呢?是不是要修改编译器的某个配置信息,然后直接用const 定义呢? A9:这个如何分配,以及用什么关键字都是由编译器决定的,如果你用的是IAR Embedded Workbench for 430,那再在数组声明的时候,前面加一个CONST那么在编译的时候就会将数组分配到FLASH空间。如果不加const默认的情况是分配到RAM空间的。

  • 2019-07-10
  • 发表了主题帖: 基于 FRAM 的 MCU MSP430FR57xx 设计

           移动信用卡读取器应用,在该应用中 MSP430FR5739 的独特差异化价值可帮助解决多种挑战。MSP430FR5739 器件配有 16K 嵌入式 FRAM、5 个独立定时器、硬件乘法器、DMA、ADC 以及串行通信端口。说到移动信用卡读取器,并非所有读取器都是同等的,它们都有一些不同的风格: (1) 简单的磁条读取器:其中该读取器仅支持一个模拟前端。卡数据从磁条读出,然后传输给电话。 (2) 磁条读取器 + 加密:其中该读取器通过 MCU 支持模拟前端,在数据传输至电话之前可对从卡中读取的数据进行加密。该实施方案已在方块图中显示。 (3) 磁条读卡器 + 加密 + 动态认证:这可通过读取被称之为堪比指纹唯一性的磁条组成信息,增加一层额外的保护。磁条信息随后可与卡信息配对,创建无法复制的特有读取数据。然而,这种方法实施起来非常复杂而且需要对现有读取器进行大规模修改,因此尚未获得广泛普及。   图中显示的是支持加密功能的读卡器。MSP430FR5739 不但可执行刷卡后的所有处理工作,而且还可只向电话传送经过加密的信息。超低功耗 MSP430 是该类应用的理想选择,因为读卡器一般通过电池供电的主机设备供电,对能源限制非常高。在诸如本应用报告介绍的一款软件中实施加密算法会消耗大量的 CPU 周期。FR5739 器件功耗不足 100uA/MHz,可在读取器不用时进入待机模式,从而可帮助读卡器满足最严格的功耗要求。3 通道 DMA 及 32 位硬件乘法器可有效减轻 CPU 负载,从而可进一步降低整体功耗。 如果写入速度快 (8MBps)、功耗低、不需要进行预擦除而且可采用字节块的方式执行,那么 FRAM 的使用就与 RAM 完全相同。在定时非常重要的应用中,非易失性存储器的写入有时会让应用处于堵塞状态,其中中断处理会被暂停,直到存储器写入或擦除完成。但使用 FRAM 就没有这种情况,因为写入可在几纳秒内完成,而且无需进行预擦除。        此外,许多读卡器还可执行某种形式的篡改检测,能够检查设备外壳是否已被打开或遭到某些方式的篡改。在 MSP430FR5739 器件上,这可使用 26 个可中断通用 I/O 引脚中的任意一个来完成。

统计信息

已有1231人来访过

  • 芯币:9078
  • 好友:--
  • 主题:2208
  • 回复:696
  • 课时:--
  • 资源:19

留言

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


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