zoukankan      html  css  js  c++  java
  • AVR速度很快功能挺强

    AVR读 写外部RAM时出现的问题

    问:
    1,对AT90S8515来说,外部RAM的地址肯定是从0x0260开始的?
    2,我用ICE200仿真8515读写外部RAM,RAM大小为32K Bytes,地址映射到0x0000-0x7fff,用PC7做它的片选。把SRE置为1,当我访问0x0300时PC7为低,这是对的,但当我不去访问0x0000-0x7fff地址空间时(例如进入死循环),问题出现了,此时PC7仍为低,按道理应为高的。这是阿AVR与51的不同,还是我弄错了。

    答:
    1、对AT90S8515来说,外部RAM的地址肯定是从0x0260开始的。这是没问题 的,访问0x0000-0x025F的地址为内部RAM。
    2、当你访问过0x0300,PC7为低,当你不访问0x0000-0x7FFF时, 由于没有一条把PC7置高的指令,所以PC7当 然还为低了。不过这也不要紧,因为当你访问0x0000-0x025F的时候,RD和WR是没有的,所以即使外部RAM被选中了,没不会产生读写。当你访问0x0260-0x7FFF的 时候,RD和WR就自动产生了。所以就是你 把外部RAM的CS直接接地也是可以的。

    2 理解#defineSREG(*(volatileunsignedchar*)0x5F

    Mon, 26 Mar 2007 15:45:57 GMT

    以前看到#define SREG    (*(volatile unsigned char *)0x5F)
    这样的定义,总是感觉很奇 怪,不知道为什么,今天终于有了一点点心得,请大虾们多多批砖~~~

       嵌入式系统编程,要求程序员能够利用C语言访问固定的内存地 址。既然是个地址,那么按照C语言的语法规则,这个表示地址的量应该是指针类型。所以,知道要访问的内存地址后,比如0x5F,
       第一步是要 把它强制转换为指针类型
     (unsigned char *)0x5F,AVR的SREG是八位寄存器,所以0x5F强制转换为指向unsigned char类型。
       volatile(可变的)这 个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。这种“意想不到地改变”,不是由程序去改变,而是由硬件去改变——意 想不到。
       第二步,对指针变量解引用,就能操作指针所指向的地址的内容了
    *(volatile unsigned char *)0x5F
       第三步,小心地把#define宏中的参数用括号括起来,这是一个很好的习惯,所 以:                     #define SREG    (*(volatile unsigned char *)0x5F)
        类 似的,如果使用一个32位处理器,要对一个32位的内存地址进行访问,可以这样定义:                #define RAM_ADDR     (*(volatile unsigned long  *)0x0000555F)
        然后就可以用C语言对这 个内存地址进行读写操作了
        读:tmp = RAM_ADDR;
        写:RAM_ADDR = 0x55;

    定义volatile是因为它的值可能会改变,大家都知道为什么改变了;
    如果在一个循环操作中需要不停地判断一个内存数据,例如要等待 SREG的I标志位置位,因为SREG也是映射在SRAM空间,为了加快速度,编译器可能会编译出这样的代码:把SREG读取到Register中,然后 不停地判断Register相应位。而不会再读取SREG,这样当然是不行了,因为程序或其它事件(中断等)会改变SREG,结果很可能是一个死循环出不 来了。如果定义成volatile型变量,编译的代码是这样的:每次要操作一个变量的时候都从内存中读取一次。
    #define SREG (*(volatile unsigned char *)0x5F) 之后,可以进行如下基本操作,
    unsigned char temp,*ptr;
    temp=SREG;把SREG值保存到temp中
    SREG=temp;把temp的值赋给SREG
    ptr = & SREG; 不知对否,大家试一下。

    3 AVR复位和中断处理

    Mon, 26 Mar 2007 12:56:10 GMT

    AVR提供了几种不同的中断源。这些中断和复位向量在程序存储器空间内都有自己单独的程序向量。所有中断都被分配一个私有的使 能位,要想使能某一中断,就要向其使能位写入逻辑1,而且要把状态寄存器中的全局中断使能位置1。

        程序存储器空间最低的一些地址,被默认定义为复位和中断向量。完整的向量列表见“中断”部分。该列表也决定了不同中断的优先级。地址越小,优先级越高。 RESET具有最高的优先级,其次是INT0——外部中断请求0。详细讨论见“中断”部分。

        当某个中断产生时,全局中断使能位I被清零,所有中断都被禁止。用户程序可以向I位写入1, 以实现中断嵌套。所有已使能的中断就可以中断当前的中断程序。当从中断指令——RETI——的执行返回时,I位被自动置位。

        基本上有两种类型的中断。第一种是由事件触发的,把中断标志置位。对于这些中断,程序计数器被引导到实际的中断向量,以执行中断处理程序,同时硬件把相应的中断标志清除。通 过向要清除的标志位位置写一个逻辑1,也可以被清除中断标志。如果中断使能位被清除后,相应的中断条件发生时,中断标志将被设置,而后保持到中断被使能为 止,或者由软件把标志清除。类似地,如果在全局中断使能位被清除后,一个或多个中断条件产生 时,相应的中断标志将被设置,并保持到全局中断使能位被设置为止,然后按优先级顺序执行。

        第二种中断,只要中断条件存在,就会被触发。这些中断没有必要具有中断标志。如果中断条件在中断被使能前消失,那么中断将不被触发。

        当AVR从一个中断中退出时,它一般会返回主程序,并且执行再执行一条指令后,才会响应后续的中断。

        注意,当进入中断程序时状态寄存器不会自动保存,当从中断程序返回时,它也不会自动恢复。这必须由用户软件来完成。

        当使用CLI指令禁能中断时,中断将立即被禁能。当CLI指令执行后,将没有中断再被执行,即使中断在CLI执行的同时发生。下例所示为怎样使用CLI指 令来避免在定时的EEPROM写时序期间避免产生中断。

     汇编代码例子
      in      r16, SREG              ; 保存SREG值 
      cli            ; 在定时程序中禁能中断
      sbi    EECR, EEMWE    ; 开始写入EEPROM
      sbi    EECR, EEWE
      out    SREG, R16            ; 恢复SREG值(I位)
    C代码例子
      char    cSREG;
      cSREG = SREG;     /* 保存SREG值*/ 
      /* 在定时程序中禁能中断 */
      _CLI ();
      EECR  |= (1<  EECR  |= (1<
      SREG  =  cSREG;                /* 恢复SREG值(I位) */

    当使用SEI指令来使能中断时,紧跟在SEI后面的指令将在任何后续的中断前被执行,示例如下。

     汇编代码例子
     sei     ; 置位全局中断使能
     sleep ; 进入休眠,等待中断
     ; 注意:将在任意中断前进入休眠
     C代码例子
       _SEI();         /* 置位全局中断使能
       _SLEEP();  /* 进入休眠,等待中断 */
      /* 注意:将在任意中断前进入休眠 */

    中断响应时间

        对于所有使能的AVR中断,中断执行响应最少为四个时钟周期。在四个时钟周期之后,实际中断处理程序的向量地址被执行。在这四个时钟周期内,程序指针 (PC)被压入堆栈。该失量正常为一到中断程序的跳转,并且该跳转花费三个时钟周期。如果中断发生在一个多周期指令的执行期间,在中断被响应前,该指令要 执行完毕。如果当MCU在休眠模式中有中断产生,那么该中断响应时间要再增加四个时钟周期。这是由于从选择的睡眠模式中唤醒需要启动时间。

        从中断处理程序返回需要四个时钟周期。在这四个时钟周期内,程序指针(两个字节)从堆栈中被弹出,堆栈指针加2,SREG的I位被置位。

    4 AVR中断应用设计要点

    Mon, 26 Mar 2007 00:22:41 GMT

    AVR的中断源比较多,尤其是ATmega128,一共有35个外部以及内部中断源。通常情况下,Flash程序存储器空间的最低位置 (0x0000-0x0045)定义为复位和中断向量空间。完整的中断向量见第二章表2-23。在中断向量表中,处于低地址的中断向量所对应的中断拥有高 优先级,所以,系统复位RESET拥有最高优先级。
    http://images.51.com/a/200609/e0/e4/vickyguan888/d4d8a78b82d53db8c723e2b346e83b3f.gif

    A. 中断设计注意点:
    1.具备Bootloader功能的AVR,其中断向量区可以在Flash程序存储器空间最低位置和Bootloader区的 头部来回迁移,这主要用于配合Bootloader程序的应用。如果不使用Bootloader功能,一般不要中断向量区进行迁移。

    2.Flash 较小的AVR芯片,其一个中断向量占据1个字的空间,用于放置一条相对转移RJMP指令(范围-2K~+2K字),跳到中断服务程序。对于不使用的中断, 在中断向量区中应放置1条中断返回指令RETI,增强程序的抗干扰性。

    3.ATmega128的Flash空间为64K字节,因此它的 一个中断向量占据2个字的空间,用于放置一条绝对转移JMP指令(指令长度为2个字),跳到中断服务程序。对于不使用的中断,在中断向量区中应连续放置2 条中断返回指令RETI,增强程序的抗干扰性。当使用汇编语言编写系统程序时应注意。

    4.当MCU响应一个中断时,其硬件系统会自动中 断返回地址压入系统堆栈,并将关闭全局中断响应(硬件将中断标志I位清0),清除该中断的中断标志位;执行中断返回指令RETI时,硬件会先允许全局中断 响应(硬件将中断标志I位置1),然后从系统堆栈中弹出返回地址到PC程序计数器中,继续执行被中断打断的程序。除此之外,MCU的硬件没有对中断保护做 其他处理。

    5.因此,用户在编写中断服务程序时,首先要编写中断现场保护程序,如保护MCU的状态寄存器等。在中断返回之前,不要忘记 恢复中断现场。

    6.如果设置和允许外部中断响应,即使是外部INT0..7引脚设置为输出方式,在引脚上的电平变化也会触发外部中断的 发生,这一特性提供了使用软件产生中断的途径。

    7.外部中断可选择采用上升沿触发、下降沿触发以及电平变化(由高变低或由低变高)和低 电平触发等方式,无外部高电平触发方式。具体触发方式由外部中断控制寄存器EICRA(INT3:0)和EICRB(INT7:4)决定。

    8. 如果选择外部低电平方式触发中断时应特别注意:(1)引脚上的低电平必须一直保持到当前一条指令执行完成后才能触发中断;(2)低电平中断并不置位中断标 志位,即外部低电平中断的触发不是由于中断标志位引起的,而是外部引脚上电平取反后直接触发中断(当然需要开放全局中断允许)。因此,在使用低电平触发方 式时,中断请求将一直保持到引脚上的低电平消失为止。唤句话说,只要中断引脚的输入引脚保持低电平,那么将一直触发产生中断。所以,在低电平中断服务程序 中,应有相应的操作命令,控制外部器件释放或取消加在外部引脚上的低电平。

    B.中断优先级以及中断嵌套处理

    1.AVR 中断的优先级由该中断向量在中断向量区中的位置确定,处于低地址的中断向量所对应的中断拥有高优先级,所以,系统复位RESET拥有最高优先级。

    2. 当两个中断同时发生申请中断时,MCU先响应中断优先级高的中断。低优先级的中断一般将保持中断标志位的状态(外部低电平中断除外),等待MCU响应处 理。

    3.MCU响应一个中断后,在进入中断服务前已由硬件自动清零全局中断允许位。因此此时即使有更高优先级的中断请求发生,MCU也 会不响应,要等执行到RETI指令,从本次中断返回,并执行了一条指令后,才能继续响应中断。所以,在缺省情况下,AVR的中断不能嵌套。AVR中断的优 先级只是在有多个中断同时发生时才起作用,此时MCU将首先响应高优先级的中断。

    4.AVR中断嵌套处理是通过软件方式实现的。如在B 中断服务中,如需要MCU能及时的响应A中断(不是等本次中断返回后再响应),B中断的服务程序应这样设计:(1)B中断的现场保护;(2)屏蔽除A以外 其它的中断允许标志;(3)用指令SEI开放允许全局中断;(4)B中断服务;(5)用指令CLI禁止全局中断(6)恢复在本中断程序被屏蔽的中断允许标 志;(7)B中断现场恢复;(8)B中断返回。

    5.采用软件方式实现中断嵌套处理的优 点,是能够让程序员可以根据不同的实际情况和需要来决定中断的重要性,有更加灵活的手段处理中断响应和中断嵌套,如让低优先级的中断(此时很重要)打断高 优先级中断的服务等,但同时也增加了编写中断服务程序的复杂性。

    6.由于AVR的指令执行速度比较高,因此在一般情况 下,不建议使用中断嵌套的处理方法。当然,这还需要用户在编写中断处理服务程序中,应遵循中断服务程序尽量要短的原则。

    C.高级语言开 发环境中中断服务程序的编写

    1.在高级语言开发环境中,都扩展和提供了相应编写中断服务程序的方法,但不同高级语言开发环境中对编写中 断服务程序的语法规则和处理方法是不同的。用户在编写中断服务程序前,应对所使用开发平台,中断程序的编写方法,中断的处理方法等有较好的了解。

    2. 使用ICCAVR、CVAVR、BASCOM-AVR等高级语言编写中断服务程序时,通常不必考虑中断现场保护和恢复的处理,因为编译器在编译中断服务程 序的源代码时,会在生成的目标代码中自动加入相应的中断现场保护和恢复的指令。

    3.如果用户要编写效率更高或特殊的中断服务程序,可以 采用嵌入汇编、关闭编译系统的自动产生中断现场保护和恢复代码等措施,但程序员要对所使用的开发环境有更深的了解和掌握,并具备较高的软件设计能力。本人 中给出的USART接收和发送中断服务程序(使用CVAVR开发平台)就是一个非常典型的示例。

    5 avr单片机中断使用心得

    Sun, 25 Mar 2007 23:59:45 GMT

    中断响应后由硬件自动清零全局中断,任何中断都无法响应,在执行完中断程序后,全局中断打开.如果需要中断嵌套,则在中断程序里软件添加打开全局中断.就可以响应任何中断(包括比本中断优先级低的中断).以 至可以中断自己嵌套自己(例如中断时间是每隔100ms一次,而中断执行时间是1s.那样中 断就自己嵌套自己,程序就混乱了).中断响应后,全局中断被屏蔽,如果还有中断进入,那么无论优先级高低都无法响应,但响应的中断标志位置位,当中断执行 完毕后,总中断打开,接着执行未响应的中断.  

    6 AVR外部SRAM

    Tue, 20 Mar 2007 22:17:54 GMT

    在实际AVR应用中很多朋友往往需要扩展external SRAM,首先我声明一点,AVR扩展SRAM的方法和51扩展的方法一样,在此不在累述。

    语言写得非常土,高手可以不看。

    下面主要讲ATmega162扩展SRAM的特点。

    先摘录datasheet里面的一段:

    An optional external data SRAM can be used with the ATmega162. This SRAM will
    occupy an area in the remaining address locations in the 64K address space. This area
    starts at the address following the internal SRAM. The Register File, I/O, Extended I/O
    and Internal SRAM uses the occupies the lowest 1280 bytes in Normal mode, and the
    lowest 1120 bytes in the ATmega161 compatibility mode (Extended I/O not present), so
    when using 64KB (65,536 bytes) of External Memory, 64,256 Bytes of External Memory
    are available in Normal mode, and 64,416 Bytes in ATmega161 compatibility mode. See
    “External Memory Interface” on page 24 for details on how to take advantage of the
    external memory map.

    翻译:ATmega162可以选择使用外部SRAM。外部SRAM会占据64K中的一部分地址空间,这个地址紧接在内部SRAM的后面。内部 Register,IO,扩展IO和内部SRAM占据了1280字节的空间,(略了161),所以当使用了全部的64KB的外部空间后,外部存储器只有 64256Byte有效。


    when the addresses accessing the SRAM memory space exceeds the internal data
    memory locations, the external data SRAM is accessed using the same instructions as
    for the internal data memory access. When the internal data memories are accessed,
    the read and write strobe pins (PD7 and PD6) are inactive during the whole access
    cycle. External SRAM operation is enabled by setting the SRE bit in the MCUCR
    Register.

    翻译:当存取SRAM的地址超出了内部数据存储器的地址的范围时,存取外部数据存储器的指令与存取内部数据存储器的指令相同(这点与51不同),当 存取内部存储器时,RD和WR在整个存取期间处于非活动状态。如果你想对外部存储器进行操作,应该怎么办? 你就要将MCUCR中的SRE位置位。(SRE: External SRAM/XMEM Enable)


    Accessing external SRAM takes one additional clock cycle per byte compared to access
    of the internal SRAM. This means that the commands LD, ST, LDS, STS, LDD, STD,
    PUSH, and POP take one additional clock cycle. If the Stack is placed in external
    SRAM, interrupts, subroutine calls and returns take three clock cycles extra because the
    2-byte Program Counter is pushed and popped, and external memory access does not
    take advantage of the internal pipeline memory access. When external SRAM interface
    is used with wait-state, one-byte external access takes two, three, or four additional
    clock cycles for one, two, and three wait-states respectively. Interrupt, subroutine calls
    and returns will need five, seven, or nine clock cycles more than specified in the instruction
    set manual for one, two, and three wait-states.

    翻译:存取外部SRAM比存取内部SRAM每个字节要额外多一个时钟周期。这意味这那几个烦人的汇编指令的执行也需要额外的一个时钟周期。如果堆栈 设定在外部SRAM,那么中断,子程序调用以及返回将要耗费三个额外的时钟周期,因为2字节的程序计数器必需要入栈和出栈,(加上那个额外的时钟,是不是 三个?嘿嘿),所以外部SRAM的存取远没有内部SRAM的流水线操作来得高效。当外部SRAM接口使用的等待功能,每字节的存取将要花去2,3或4个额 外的时钟周期。随即中断、子程序调用---大家不看也知道下面什么意思了,嘿嘿!

    Using all Locations of
    External Memory Smaller than
    64 KB

    Since the external memory is mapped after the internal memory as shown in Figure 11,
    the external memory is not addressed when addressing the first 1,280 bytes of data
    space. It may appear that the first 1,280 bytes of the external memory are inaccessible
    (external memory addresses 0x0000 to 0x04FF). However, when connecting an external
    memory smaller than 64 KB, for example 32 KB, these locations are easily accessed
    simply by addressing from address 0x8000 to 0x84FF. Since the External Memory
    Address bit A15 is not connected to the external memory, addresses 0x8000 to 0x84FF
    will appear as addresses 0x0000 to 0x04FF for the external memory. Addressing above
    address 0x84FF is not recommended, since this will address an external memory location
    that is already accessed by another (lower) address. To the Application software,
    the external 32 KB memory will appear as one linear 32 KB address space from 0x0500
    to 0x84FF. This is illustrated in Figure 17. Memory configuration B refers to the
    ATmega161 compatibility mode, configuration A to the non-compatible mode.

    翻译:因为外部存储器在那个图中的内部存储器之后才被映射,所以呢,在外部存储器的头1280字节的空间是不可以寻址的,这就表现为不能存取外部存 储器的0X0000-0X04FF。当然有个特殊的情况,当使用的外部存储器小于64K(典型值32K)时,外部存储器的0X0000-0X04FF范围 可以通过访问0x8000 to 0x84FF进行存取(想一想为什么?)。因为高位地址线A15没有接入,外部寻址范围只有0X0000-0X7FFF,当访问0x8000 to 0x84FF时,低14位地址线定位在外部0X0000-0X04FF处(或许大家会疑惑,怎么不是访问的内部0X0000-0X04FF?请注意了,我 们现在访问的是外部存储器,因为地址范围为0x8000 to 0x84FF,不是内部存储器,所以自然不是定位在内部0X0000-0X04FF范围)。不推荐大家对0x84FF以外的范围进行访问,因为这将改变 0X04FF-0X7FFF的数据,因为低14位地址是重复的。在应用中,地址在0x0500 to 0x84FF范围的存取是连续的线性的,只有0X0000-0X04FF有写特殊。大家可以参考下图。

    7 ATEMGA128-16读写外部32KRAM-70NS的完整测试程序

    Tue, 20 Mar 2007 16:36:11 GMT

    地址分配 
    0X0000-0X10FF 内部4KRAM 
    0X1100-0X7FFF 外部32KRAM 

    系 统频率16M ,BAUD=9600,下面对程序对0/1/2/2+1等待状态下,RAM进行测试,全部通过!! 

    //ICC-AVR application builder : 2006-8-14 16:03:08
    // Target : M128
    // Crystal: 16.000Mhz

    //1.debug rs232
    //

    #include<iom128v.h>
    #include<macros.h>
    #include<stdio.h>
    #include<string.h>

    //MCU时钟频率
    #define F_CPU   16000000
    //默认的系统BAUD
    #define baud    115200
          
    #define MCUBAUD9600 1  

    //declare memory mapped variables
    #define txbuf1_head 0x1100
    //外部RAM大小0X7FFF-0X1100=0X6EFF=28415byte

    extern volatile unsigned char txbuf1[28415];

    //define mappings
    void mapping_init(void)
    {

     asm(
      ".area memory(abs)\n"
      ".org 0x1100\n"
      " _txbuf1:: .blkb 28415\n"
      ".text\n"
     );
     
    }
    //定义外部RAM地址
    #define ext_PORT1 ((volatile unsigned char *)0x1100)
    //定义一个指针指向外部RAM首地址
    unsigned char *p=(unsigned char *)ext_PORT1;
    //RAM测试的读写数据
    unsigned char testramtable[4]={0x00,0x55,0xaa,0x00};

    void Delay(void)
    {
    }
    void Delay1ms(void)
    {    
    unsigned int  i;

    for(i=0;i<=(unsigned int)(16*143-2);i++);
    }

    void Delayxms(unsigned char ms)
    {   
    unsigned char i;

    for(i=0;i<=ms;i++)Delay1ms();

    }


    //unsigned char  readram(unsigned int iaddr);
    void port_init(void);
    void watchdog_init(void);
    void uart1_init(void);
    //void writeram(unsigned int iaddr, unsigned char ctemp);
    void RAM_TEST(unsigned char );
    void test_net(void);
    //void sendstring1(unsigned int * txbuf);
    char char2hex(char t1)
    {
    if((t1>=00) &&(t1<=0x09))t1=t1+0x30;//'0'--'9'
    else 
    {
     
     if((t1>=0x0a)&&(t1<=0x0f))//'A'--'F'
     t1=t1-0x0a+0x61;
    }
    return t1;
    }

    void sendinthex1(int c)
    {
    char temph=0,templ=0;
    char t1=0,t2=0;

    temph=c/256;
    templ=c%256;

    t1=(c/256)/16;
    //t1=t1>>8;
    UDR1 = char2hex(t1);
    while(!(UCSR1A & 0x40));
    UCSR1A |=0x40;

    t1=(c/256)%16;
    UDR1 = char2hex(t1);
    while(!(UCSR1A & 0x40));
    UCSR1A |=0x40;

    t2=(c%256)/16;//templ&0xf0;
    //t2=t2>>8;
    UDR1 = char2hex(t2);
    while(!(UCSR1A & 0x40));
    UCSR1A |=0x40;

    t2=(c%256)%16;//templ&0x0f;
    UDR1 = char2hex(t2);
    while(!(UCSR1A & 0x40));
    UCSR1A |=0x40;

    }

    void sendchar1(char c) // 发送 
    {
    UDR1 = c;
    while(!(UCSR1A & 0x40));
    UCSR1A |=0x40;
    }

    void sendint1( int c) // 发送 
    {
    UDR1 = (c&0xff00)>>8;
    while(!(UCSR1A & 0x40));
    UCSR1A |=0x40;

    UDR1 = c&0xff;
    while(!(UCSR1A & 0x40));
    UCSR1A |=0x40;


    }


    void sendstring1(unsigned char * txbuf) // 发 送 
    {
    unsigned int j;
    for (j = 0; *txbuf; j++, txbuf++)sendchar1(*txbuf);
    //for(;*txbuf!='/0';txbuf++)

    }

    void port_init(void)
    {
    //PA AD0-AD7  地址
    //PC AD8-AD15
     PORTA = 0xFF;
     DDRA  = 0xFF;

     PORTC = 0xFF; //m103 output only
     DDRC  = 0x00;

    //PB4 NETRST O
     
     PORTB = 0xFF;
     DDRB  = 0x10;
     
     PORTD = 0xFF;
     DDRD  = 0x00;
    // PE0 RXD0 
    // PE1 TXD0
    // PE4 NET_IRQ  i
    // PE5 INFRA_IRQ  i
    // 
     PORTE = 0xFF;
     DDRE  = 0x00;
     
     PORTF = 0xFF;
     DDRF  = 0x00;
     
     PORTG = 0x1F;
     DDRG  = 0x00;
    }

    //Watchdog initialisation
    // prescale: 2048K cycles
    void watchdog_init(void)
    {
     WDR(); //this prevents a timout on enabling
     //WDTCR = 0x0F; //WATCHDOG ENABLED - dont forget to issue WDRs
     /* reset WDT */

    /* Write logical one to WDTOE and WDE */
    //WDTCR |= (1<<WDTOE) | (1<<WDE);
    WDTCR=0X18;   //现在把WDTCH给关掉了
    /* Turn off WDT */
    WDTCR = 0x00;
    //WDTCR=0X17;

    }
    //UART0 initialisation
    // desired baud rate:115200
    // actual baud rate:111111 (3.7%)
    // char size: 8 bit
    // parity: Disabled
    /*
    void uart0_init(void)
    {
     UCSR0B = 0x00; //disable while setting baud rate
     UCSR0A = 0x00;
     UCSR0C = 0x06;

    // UBRRL = (fosc / 16 / (baud + 1)) % 256; 
    // UBRRH = (fosc / 16 / (baud + 1)) / 256; 

     UBRR0L = (F_CPU / 16 / (baud + 1)) % 256;//0x03;//0x08; //set baud rate lo
     UBRR0H = (F_CPU / 16 / (baud + 1)) / 256;//0x00; //set baud rate hi
     UCSR0B = 0x18;//0x98;
    }
    */
    //UART1 initialisation
    // desired baud rate:115200
    // actual baud rate:111111 (3.7%)
    // char size: 8 bit
    // parity: Disabled
    void uart1_init(void)
    {
     
    #ifdef MCUBAUD9600
    // UBRRL = (fosc / 16 / (baud + 1)) % 256; 
    // UBRRH = (fosc / 16 / (baud + 1)) / 256; 
     UCSR1B = 0x00; //disable while setting baud rate
     UCSR1A = 0x00;
     UCSR1C = 0x06;
     UBRR1L = 0x67; //set baud rate lo
     UBRR1H = 0x00; //set baud rate hi
     UCSR1B = 0x18;
    #else 
    //baud115200
     UCSR1B = 0x00; //disable while setting baud rate
     UCSR1A = 0x00;
     UCSR1C = 0x06;
     UBRR1L = (F_CPU / 16 / (baud + 1)) % 256;//0x03;//0x08; //set baud rate lo
     UBRR1H = (F_CPU / 16 / (baud + 1)) / 256;//0x00; //set baud rate hi
     UCSR1B = 0x18;//0x98;
    #endif 
    }
    /*
    #pragma interrupt_handler int0_isr:2
    void int0_isr(void)
    {
     //external interupt on INT0
    }
    */


    //call this routine to initialise all peripherals
    void init_devices(void)
    {
     //stop errant interrupts until set up
     CLI(); //disable all interrupts
     XDIV  = 0x00; //xtal divider
     port_init();
      mapping_init();
     watchdog_init();
     uart1_init();

     //External RAM will reside between 8000h - FFFFh.
    //There will be 2 wait states for both read and write.

    // MCUCR = 0x80;
     //EICRA = 0x03; //extended ext ints
     //EICRB = 0x00; //extended ext ints
     //EIMSK = 0x01;
     //TIMSK = 0x00; //timer interrupt sources
     //ETIMSK = 0x00; //extended timer interrupt sources
     SEI(); //re-enable interrupts
     //all peripherals are now initialised
     

     
     
    }



    //测试RTL8019AS
    void test_net(void)
    {

    }

    //
    void RAM_TEST(unsigned char umode)

     unsigned int k=0;
     unsigned int i=0,j=0;
     unsigned char DATA,u; 
    // unsigned char *p=(unsigned char *)ext_PORT1;
    // unsigned char *pointer=p;
     
     sendstring1("init system ok!\n");
     sendstring1("now test system-ram  all is 32k !\n");
    for(u=0;u<4;u++)
    {
       
       sendstring1("----now write  ram  ");
       sendinthex1(testramtable[u]);
       sendstring1("\n");
       
       i=0;
       do
       {   if(!umode)*(p+i)=testramtable[u];//testok
             else  txbuf1[i]=testramtable[u];
             i++;
       }while(i<0x6f00);//while(i<0x6eff);//while(i>0x6eff);
       
       sendstring1("----write ok\n");
       sendstring1("----now check write\n");
       
       
        k=0x1100;
          i=0;
          do
          { 
               if(!umode)DATA = *(p+i);//test ok
               else     DATA=txbuf1[i];
               if(DATA!=testramtable[u])
               {sendstring1("addr = ");
               sendinthex1(k);
            //sendstring1(" =");
            //sendinthex1(DATA);
               sendstring1("\n");
               }
            k++;i++; 
           }while(k<0x8000);//while(k<0x1110);
          //0x7fff);
          
     sendstring1("---- test system-ram  end!\n");
    }
    }

    void main(void)
    {

     init_devices();
     
     sendstring1("RAMTEST START !\n");
     sendstring1("-----POINTER  READ AND WRITE EXTERNAL 32K RAM----------\n");
     sendstring1("0 READ AND WRITE NO WAIT  PC7 RELEASED !\n");
     MCUCR = 0x80; // 允 许外部并行扩展接口,忽略高位0X8000的等待时间
     XMCRA = 0x40; //0x00 external memory
     XMCRB = 0x01; // 释 放PC7,作为通用I/O引脚使用 
     DDRC =  0xff; // PC7用于输出,(不影响PC0-PC6地址线) 
     PORTC = 0x00; // PC7 输出0,(不影响PC0-PC6地址线) 
     RAM_TEST(0);

     sendstring1("1 READ AND WRITE 1 WAIT  PC7 RELEASED !\n");
     MCUCR = 0x80; // 允许外部并行扩展接口,忽略高位0X8000的等待时间
     XMCRA = 0x44; //0x00 external memory
     XMCRB = 0x01; // 释放PC7,作为通用I/O引脚使用 
     DDRC =  0xff; // PC7,PC6用 于输出,(不影响PC0-PC5地址线) 
     PORTC = 0x00; // PC7,PC6输出0,(不影响PC0-PC5地址线) 
     RAM_TEST(0);


     sendstring1("2 READ AND WRITE 2 WAIT  PC7 RELEASED !\n");
     MCUCR = 0x80; // 允 许外部并行扩展接口,忽略高位0X8000的等待时间
     XMCRA = 0x48; //0x00 external memory
     XMCRB = 0x01; // 释 放PC7,作为通用I/O引脚使用 
     DDRC =  0xff; // PC7,PC6用于输出,(不影响PC0-PC5地址线) 
     PORTC = 0x00; // PC7,PC6 输出0,(不影响PC0-PC5地址线) 
     RAM_TEST(0);

     
     sendstring1("3 READ AND WRITE 2+1 WAIT  PC7 RELEASED !\n");
     MCUCR = 0x80; // 允许外部并行扩展接口,忽略高位0X8000的等待时间
     XMCRA = 0x4C; //0x00 external memory
     XMCRB = 0x01; // 释放PC7,作为通用I/O引脚使用 
     DDRC =  0xff; // PC7,PC6用 于输出,(不影响PC0-PC5地址线) 
     PORTC = 0x00; // PC7,PC6输出0,(不影响PC0-PC5地址线) 
     RAM_TEST(0);
     sendstring1("******POINTER  READ AND WRITE EXTERNAL 32K RAM*******\n");
     
     sendstring1("\n-----BUFFER READ AND WRITE EXTERNAL 32K RAM----------\n");
     sendstring1("4  READ AND WRITE  NOWAIT PC7  NO RELEASED !\n");
     MCUCR = 0x80; // 允 许外部并行扩展接口,忽略高位0X8000的等待时间
     XMCRA = 0x40; //external memory
     RAM_TEST(1);

     sendstring1("5  READ AND WRITE  1 WAIT PC7  NO RELEASED !\n");
     MCUCR = 0x80; // 允许外部并行扩展接口,忽略高位0X8000的等待时间
     XMCRA = 0x44; //external memory
     RAM_TEST(1);

     sendstring1("6  READ AND WRITE  2WAIT PC7  NO RELEASED !\n");
     MCUCR = 0x80; // 允许外部并行扩展接口,忽略高位0X8000的等待时间
     XMCRA = 0x48; //external memory
     RAM_TEST(1);

     sendstring1("7  READ AND WRITE  2+1WAIT PC7  NO RELEASED !\n");
     MCUCR = 0x80; // 允许外部并行扩展接口,忽略高位0X8000的等待时间
     XMCRA = 0x4A; //external memory
     RAM_TEST(1);
     sendstring1("\n*******BUFFER READ AND WRITE EXTERNAL 32K RAM***********\n");
     
     sendstring1("---- RAM TEST OK!-----\n");

    //while(1){}

    // sendstring1("now test rtl8019as!\n");
    // test_net();
    //sendstring1("----test rtl8019as end!\n");

    }

    8 volatile的使用

    Tue, 20 Mar 2007 14:58:13 GMT

    变量定位

    普通变量的定义和访问同标准C语言,在HCS08 C语言中我们主要要解决映像寄存器变量和某些特殊变量的定位问题,即把这些变量存放在RAM中指定的位置。

    1映像寄存器定位

    映像寄存器单片机中跟硬件有关的寄存器,它们都有各自固定RAM地址,其定位有3种方法

    1)宏定义

    例如:#define PortA ( * ( volatile unsigned char * )  0x0000 )

    这样 PortA 成为一个地址在0x0000的unsigned char类型变量。这个定义看起来很复杂,其实它也可以分解成几个很简单的部分来看。 ( volatile unsigned char * )是C语言中的强制类型转换,它的作用是把0x0000这个纯粹的十六进制数转换成为一个(地址)指针,其中volatile并不是必要的,它只是告诉编 译器,这个值与外界环境有关,不要对它优化接下来在外面又加了一个*号,就表示0x0000内存单元中的内容了。经过这个宏定义之后,PortA就被可以 做为一个普通的变量来操作,所有出现PortA的地方编译的时候都被替换成( * ( volatile unsigned char * )  0x0000 ),外面一层括号是为了保证里面的操作不会因为运算符优先级或者其它不可预测的原因被改变而无法得到预期的结果。

    这种定义方法适合所有的C编译器,可移植性好,但PortA并不是一个真正的变量,只是一个宏名,当你调试一个程序的时候,无法在调试窗口观察它的 值。另外连接器也失去了灵活性,它得防止其它变量跟此变量冲突。

    2)使用@关键字

    例如: volatile unsigned char PortA       @0x0000;

    @是编译器扩展的一个特殊修饰符,其它编译器很可能并不认识。这种定义具有很好的可读性,失去了可移植性。

    3)使用段定义

    这种方法分为2个步骤

    首先把变量定义在段中,其次在连接参数文件(*.prm)中把段定位在一个合适的位置

    例如:第1步:在源程序文件中

    #pragma DATA_SEG PORTB_SEG

    volatile unsigned char PortA;

    #pragma DATA_SEG DEFAULT

    这样变量 PortA 定义在段 PORTB_SEG 中

    第2步:在 prm 文件中

    SECTIONS

    PORTB_SEG = READ_WRITE 0x0000 SIZE 1;

    这样段 PORTB_SEG 定位在地址0x0000上。

    这种方法可移植性也很差,如果你要把它移植在别的编译器上,你不得不修改源程序。

    2变量定义修饰符

    变量定义有三个修饰符值得注意,虽然它们与标准C是相同的,但是在嵌入式C语言中又有不同的含义。

    1) static

    在子函数中static用声明的变量是局部变量,但是退出这个子函数后其值不消失。下一次调用这个函数时仍可以访问到原来的值。注意,在子函数中声 明的static变量只对声明他的函数可见,别的函数是不可以使用的。如果static变量是在模块中声明的,那么只有本模块的函数可以使用它,别的模块 中的函数是不能访问的。

    void MyFunction (void)

      static char myVar = 0;  //用 static声明的局部变量

      myVar = myVar + 1;

    }

    void main (void)

    {

       MyFunction();      //调用之前myVar = 0,调用之后myVar = 1

       MyFunction();      //调用之前myVar = 1,调用之后myVar = 2

    }

    2) volatile

    如果一个变量的值可能会被程序操作之外的其它操作所改变,那么你必需用volatile 声明。在嵌入式系统中其它操作是:中断服务程序的操作、硬件动作的操作。

    用volatile声明的变量是不会被编译器优化掉的,如:

    volatile unsigned char PortA       @0x0000;

    PORTA做为一个输入端口,其值是由外部设备决定的,由于外部设备的变化是随机的,因此第一次读取的值和第二次读取的值很可能不同,所以我们把它 声明为volatile变量。

    a = PORTA;  

    a = PORTA;

    由于PORTA是用volatile声明的变量,编译器不会把它优化成一句,而如果不是volatile声明的编译器就会将第二句优化掉,从而程序 将会忽略输入端口的变化。

    通常把嵌入式设备的所有外围器件寄存器都声明为volatile 的。

    3) const

    修饰符 const 可以用在任何变量之前, 告诉编译器把此变量存储在ROM中。ROM_VAR段是定位 const 变量的默认段

    语法格式:#pragma CONST_SEG <段名>

    例如:

    #pragma DATA_SEG DEFAULT

    #pragma CONST_SEG DEFAULT

    static int a;//变量 a 存放在默认的 RAM 段 DEFAULT_RAM 中,DEFAULT_RAM是段名

    static const int c0 = 10;//变量 c0 存放在默认的 ROM 段 ROM_VAR 中,ROM_VAR是段名

    此时编译器选项-Cc必需是打开的。如果编译器选项-Cc必需是关闭的,则变量a和c0都定位在DEFAULT_RAM中。

    例如:

    #pragma DATA_SEG MyVarSeg

    #pragma CONST_SEG MyConstSeg

    static int a; //变量 a 存放在段MyVarSeg中,MyVarSeg是段名

    static const int c0 = 10; //变量 c0 存放在段 MyConstSeg 中,MyConstSeg是段名

    此时编译器选项-Cc必需是打开的。如果编译器选项-Cc必需是关闭的,则变量a和c0都定位在MyVarSeg中。

    3全局变量和局部变量

    全局变量为整个程序而定义,在整个程序运行期间。它们占用固定的RAM资源,因此除非在必需的情况,否则不要轻易使用。局部变量为某个函数而定义, 只在此函数运行的时候,占用栈空间,局部变量实质上是函数运行所需要的一段RAM空间。因此函数不运行时,它们不占用RAM资源。全局变量通常是为了给中 断服务函数传递参数定义的,建议定义时把它们定义在一个相对集中的RAM空间。

    例如:

    #pragma DATA_SEG SCI_DATA /* 这条预处理指令之后的所有变量定义在 SCI_DATA 段 */

    char senderBuffer[50];

    char receiverBuffer[100];

    ...

    #pragma DATA_SEG DEFAULT /*这条预处理指令之后的所有变量定义在默认段*/

    在参数文件中必需指定 SCI_DATA 段。

    4位定义和访问

    HCS08 C 语言采用直接位访问的方法来访问位,位的定义采用联合和结构数据类型来实现。

    例如:

    volatile union {

    struct {

    unsigned char MWPR:1; /* MWPR is bit 0 in FEETST */

    unsigned char STRE:1; /* STRE is bit 1 in FEETST */

    unsigned char VTCK:1; /* VTCK is bit 2 in FEETST */

    unsigned char FDISVFP:1; /*FDISVFP is bit 3 in FEETST */

    unsigned char FENLV:1;/* FENLV is bit 4 in FEETST */

    unsigned char HVT:1; /* HVT is bit 5 in FEETST */

    unsigned char GADR:1; /* GADR is bit 6 in FEETST */

    unsigned char FSTE:1; /* FSTE is bit 7 in FEETST */

    } FEETST_BITS;

    unsigned char FEETST_BYTE; /* Alternate definition of the

    port as a 8-bit variables. */

    } FEETST_struct @0x00F6;

    /* Define Symbol to access register FEETST */

    #define FEETST FEETST_struct.FEETST_BYTE

    /* Define Symbols to access single bits in FEETST */

    #define MWPR FEETST_struct.FEETST_BITS.MWPR

    #define STRE FEETST_struct.FEETST_BITS.STRE

    #define VTCK FEETST_struct.FEETST_BITS.VTCK

    #define FDISVFP FEETST_struct.FEETST_BITS.FDISVFP

    #define FENLV FEETST_struct.FEETST_BITS.FENLV

    #define HVT FEETST_struct.FEETST_BITS.HVT

    #define GADR FEETST_struct.FEETST_BITS.GADR

    #define FSTE FEETST_struct.FEETST_BITS.FSTE

    这里的“:1”表示仅需要一个位,HCS08会把它们包装在一起形成一个字节。这样我们就可以以字节方式或位方式访问整个寄存器和位。

    例如:

    FEETST =0X80;//字节方式

    MWPR = 1;//位方式

    VTCK=1; // BSET 2,FEETST1

    FDISVFP=0; // BCLR 4,FEETST1

    有关映像寄存器和位定义可以参照具体芯片的头文件。

    9 AVRSTUDIO调试点滴-Creatinganewproject

    Sat, 17 Mar 2007 11:20:55 GMT

    1. Create a new project by selecting 'New Project.' from the Project menu.
      This will open the Project Wizard.

       

      点击看大图

    2. Select AVR GCC as the 'Project Type', specify a project name and select a location for the project. An initial file with the same name as the project will be created and added to the project by default. If you don't want this file to be added to the project you can just clear the 'Create Initial File' box. If you want to change the name of the initial file, just edit the 'Initial File' text.
      Press 'Finish' when you are satisfied with the settings.

       
    3. The Project Tree will be set up. Notice that the initial file created in step 2. has been added to the 'Source Files' tree node.  All source files will be placed under this node regardless of their position on disk. Header files will be put under 'Header Files'.
      Also, the initial file has been opened in the editor.

       
    4. Now, write the following code into the open editor window:

      #include <avr/io.h>
      int main(void)
      {
        DDRB = 0xFF;
        while (1)
        {
           PORTB = PIND;
        }
      }

       
    5. Compile the project by pressing the compile button

      点击看大图

       
      You should now be ready to start debugging the code by pressing the 'start debugging button'. Change build options by opening the Configuration Options Dialogue.
       

    10 EEPROM的操作

    Wed, 14 Mar 2007 14:27:54 GMT

    本程序简单的示范了如何使用ATMEGA16的EERPOM
         EEPROM的简介
         EEPROM的写操作
         EEPROM的读操作

    出于简化程序考虑,各种数据没有对外输出,学习时建议使用JTAG ICE硬件仿真器
     在打开调试文件到JTAG后,
     打开 Debug -> JTAG ICE Options菜单,
     然后在JTAG ICE Properties中点击Dbug页面,将preserve eeprom选项选中。
     在每次仿真调试时候,就保护EEPROM内容了。
     否 则,会按照默认设置擦除EEPROM的内容。
     
     由于定义了EEPROM变量,JTAG调试时会询问是否初始化EEPROM,请选择 [否]
     
     EEPROM的数据也可以在view->memory,选Eeprom窗口下察看
    */

    #include <avr/io.h>
    #include <avr/eeprom.h>
    ////时钟定 为内部1MHz,F_CPU=1000000  时钟频率对程序的运行没什么影响
    /*
    GCCAVR(avr-libc)里面自带了 EEPROM的读写函数。
    下面列举部分常用函数(原型)

    #define eeprom_is_ready() bit_is_clear(EECR, EEWE)
    检测EEPROM是否准备好。 OK返回1(返回EEWE位)

    #define eeprom_busy_wait() do {} while (!eeprom_is_ready())
    等待 EEPROM操作完成

    extern uint8_t eeprom_read_byte (const uint8_t *addr);
    读取指定地址的一个字节 8bit的EEPROM数据

    extern uint16_t eeprom_read_word (const uint16_t *addr);
    读取指定地址的一个 字16bit的EEPROM数据

    extern void eeprom_read_block (void *buf, const void *addr, size_t n);
    读取由指定地址开始的指定长度的EEPROM数据

    extern void eeprom_write_byte (uint8_t *addr, uint8_t val);
    向指定地址写 入一个字节8bit的EEPROM数据

    extern void eeprom_write_word (uint16_t *addr, uint16_t val);
    向指定地 址写入一个字16bit的EEPROM数据

    extern void eeprom_write_block (const void *buf, void *addr, size_t n);
    由指定地址开始写入指定长度的EEPROM数据

    但不支持部分AVR,原文如下:
    \note This library will \e not work with the following devices since these
        devices have the EEPROM IO ports at different locations:

        - AT90CAN128
        - ATmega48
        - ATmega88
        - ATmega165
        - ATmega168
        - ATmega169
        - ATmega325
        - ATmega3250
        - ATmega645
        - ATmega6450
    */

    /*
    在程序中对EEPROM 操作有两种方式
    方式一:直接指定EERPOM 地址
        即读写函数的地址有自己指定,用于需要特定数据排列格式的应用中
    方式二:先定义EEPROM 区变量法
     在这种方式下变量在EEPROM 存储器内的具体地址由编译器自动分配。
     相对方式一,数据在EEPROM 中的具体位置是不透明的。
     为 EEPROM 变量赋的初始值,编译时被分配到.eeprom 段中,
     可用avr-objcopy 工具从.elf文件中提取并产生ihex 或binary 等格式的文件,
     从而可以使用编程器或下载线将其写入到器件的EEPROM 中。
     实际上WINAVR 中MFILE 生成的MAKEFILE 已经为我们做了这一切。
     它会自动生成以 “.eep” 为后缀的文件,通常它是iHex 格式
     (这次测试发 现 分配地址是从0x0000开始的,故增加了一个EEPROM变量Evalvoid[16])
     
    如果同时使用方式1和2,请注意防止地 址重叠,自己指定的地址应该选在后面。
    */

    //全局变量
    unsigned char EDATA;

    unsigned char ORGDATA[16]={0x00,0x02,0x04,0x06,0x08,0x0A,0x0C,0x0E,
             0x01,0x03,0x05,0x07,0x09,0x0B,0x0D,0x0F}; //原始数据

    unsigned char CMPDATA[16];            //比较数据

    //仿真时在watch窗口,监控这些全局变量。

    //EEPROM 变量定义
    unsigned char Evalvoid[16] __attribute__((section(".eeprom"))); //这个没用到
    unsigned char Eval[16] __attribute__((section(".eeprom")));

    int main(void)
    {
     eeprom_write_byte (0x40,0xA5);  //向EEPROM的0x40地址写入数据 0xA5
     EDATA=eeprom_read_byte (0x40);  //读出,然后看看数据对不对?
     //上面两句编译是有如下警告,但不必理会
     //EEPROM_main.c:103: warning: passing arg 1 of `eeprom_write_byte' makes pointer from integer without a cast
     //EEPROM_main.c:104: warning: passing arg 1 of `eeprom_read_byte' makes pointer from integer without a cast
     
     eeprom_write_block (&ORGDATA[0], &Eval[0], 16);  //块写入
     //看看EEPROM数据是否是能失电永久保存, 可以注释上面这句程序(不写入,只是读出),然后编译,烧写,断电(一段时间),上电,调试。
     eeprom_read_block (&CMPDATA[0],&Eval[0], 16);    //块读出,然后看看数据对不对?
     
        while (1);   
    }

    /*
    ATmega16 包含512 字节的EEPROM 数据存储器。
    它是作为一个独立的数据空间而存在的,可以按字节读写。
    EEPROM 的寿命至少为100,000 次擦除周期。
    EEPROM的访问由地址寄存器EEAR、数据寄存器EEDR和控制寄存器EECR决定。
    也可 以通过ISP和JTAG及并行电缆来固化EEPROM数据

    EEPROM数据的读取:
     当EEPROM地址设置好之后,需置位EERE以便将数据读入EEDR。
     EEPROM数据的读取需要 一条指令,且无需等待。
     读取EEPROM后CPU 要停止4 个时钟周期才可以执行下一条指令。
    注意:用户在读取EEPROM 时应该检测EEWE。如果一个写操作正在进行,就无法读取EEPROM,也无法改变寄存器EEAR。

    EEPROM数据的写入:
    1 EEPROM的写访问时间(自定时时间,编程时间)
        自定时功能可以让用户软件监测何时可以开始写下一字节。(可以采用中断方式)
     经过校准的1MHz片内振荡器用于EEPROM定时,不倚赖 CKSEL熔丝位的设置。
     改变OSCCAL寄存器的值会影响内部RC振荡器的频率因而影响写EEPROM的时间。
     EEPROM自定时 时间约为8.5 ms 即1MHz片内振荡器的8448个周期
    注意:这个时间是硬件定时的,数值比较保险,其实真正的写入时间根本就用不了 8.5mS那么长,而且跟电压有关,但芯片没有提供其他的检测编程完成的方法
         这个问题表现在旧版的AT90S系列上面,由于没有自定时,数值定得太短,ATMEL给人投诉到头都爆,呵呵!
    参考: 《用ATmega8535替换AT90S8535》文档里面的
         写EEPROM定时的改进
         在AT90S8535中写EEPROM的时间取决于供电电压,通常为2.5ms@VCC=5V4ms@VCC=2.7V
         ATmega8535中写EEPROM的时间为8448个校准过的RC振荡器周期 (与系统时钟的时钟源和频率无关)。
         假定校准过的RC振荡器为1.0MHz,则写时间的典型值为8.4ms,与VCC 无关。
        
    2 为了防止无意识的EEPROM 写操作,需要执行一个特定的写时序
         (如果使用编译器的自带函数,无须自己操心)
     写时序如下( 第3 步和第4 步的次序并不重要):
     1. 等待EEWE 位变为零
     2. 等待SPMCSR 中的SPMEN 位变为零
     3. 将新的EEPROM 地址写入EEAR( 可选)
     4. 将新的EEPROM 数据写入EEDR( 可选)
     5. 对EECR 寄存器的EEMWE 写"1",同时清零EEWE
     6. 在置位EEMWE 的4 个周期内,置位EEWE
     经过写访问时间之 后,EEWE 硬件清零。
     用户可以凭借这一位判断写时序是否已经完成。
     EEWE 置位后,CPU要停止两个时钟周期才会运行下一条指令。


    注意:
     1:在CPU 写Flash 存储器的时候不能对EEPROM 进行编程。
       在启动EEPROM 写操作之前软件必须检查 Flash 写操作是否已经完成
          步骤(2) 仅在软件包含引导程序并允许CPU对Flash 进行编程时才有用。
          如果CPU 永远都不会写Flash,步骤(2) 可省略。
         
     2:如果在步骤5 和6 之间发生了中断,写操作将失败。
          因为此时EEPROM 写使能操作将超时。
          如果一个操作EEPROM的中断打断了另一个EEPROM操作,EEAR 或EEDR寄存器可能被修改,引起EEPROM 操作失败。
          建议此时关闭全局中断标志I。
          经过写访问时间之后,EEWE 硬件清零。用户可以凭借这一位判断写时序是否已经完成。
          EEWE 置位后,CPU要停止两个时钟周期才会运行下一条指令。
        

    在掉电休眠模式下的EEPROM写操作
     若程序执行掉电指令时EEPROM 的写操作正在进行, EEPROM 的写操作将继续,并在指定的写访问时间之前完成。
     但写操作结束后,振荡器还将继续运行,单片机并非处于完全的掉电模式。因此在执行掉电指令之前 应结束EEPROM 的写操作。
     
    防止EEPROM数据丢失
     若电源电压过低,CPU和EEPROM有可能工作不正常,造成 EEPROM数据的毁坏(丢失)。
       **这种情况在使用独立的EEPROM 器件时也会遇到。因而需要使用相同的保护方案。
     由于电 压过低造成EEPROM 数据损坏有两种可能:
      一是电压低于EEPROM 写操作所需要的最低电压;
      二是CPU本身已经无法正常 工作。
     EEPROM 数据损坏的问题可以通过以下方法解决:
      当电压过低时保持AVR RESET信号为低。
      这可以通过 使能芯片的掉电检测电路BOD来实现。如果BOD电平无法满足要求则可以使用外部复位电路。
      若写操作过程当中发生了复位,只要电压足够高,写 操作仍将正常结束。
       (EEPROM在2V低压下也能进行写操作---有可以工作到1.8V的AVR芯片)
      
    掉电检测 BOD的误解
      AVR自带的BOD(Brown-out Detection)电路,作用是在电压过低(低于设定值)时产生复位信号,防止CPU意外动作.
      对EEPROM的保护作用是当电压过低时保 持RESET信号为低,防止CPU意外动作,错误修改了EEPROM的内容
      
      而我们所理解的掉电检测功能是指 具有预测功能的可以进行软件处理的功能。
      例如,用户想在电源掉电时把SRAM数据转存到EEPROM,可行的方法是
         外接一个在4.5V翻转的电压比较器(VCC=5.0V,BOD=2.7V),输出接到外部中断引脚(或其他中断)
         一但电压低于4.5V,马上触发中断,在中断服务程序中把数据写到EEPROM中保护起来
     注意: 写一个字节的EEPROM时间长达8mS,所以不能写入太多数据,电源滤波电容也要选大一些
    */

    大部分转载 小部分自写
  • 相关阅读:
    topcoder srm 681 div1
    topcoder srm 683 div1
    topcoder srm 684 div1
    topcoder srm 715 div1
    topcoder srm 685 div1
    topcoder srm 687 div1
    topcoder srm 688 div1
    topcoder srm 689 div1
    topcoder srm 686 div1
    topcoder srm 690 div1 -3
  • 原文地址:https://www.cnblogs.com/8586/p/1685585.html
Copyright © 2011-2022 走看看